evolcore 0.0.19 → 0.0.21

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 (136) hide show
  1. package/CHANGELOG.md +43 -1
  2. package/README.md +60 -9
  3. package/bin/install-codex-managed-hooks.mjs +61 -0
  4. package/dist/agents/baseagent.js +10 -6
  5. package/dist/agents/claude-runner.js +383 -111
  6. package/dist/agents/codex-app-server-client.js +10 -2
  7. package/dist/agents/codex-runner.js +405 -137
  8. package/dist/agents/ecagent-runner.js +174 -63
  9. package/dist/agents/gemini-runner.js +130 -30
  10. package/dist/agents/request-identity.js +25 -0
  11. package/dist/agents/runner-types.js +19 -0
  12. package/dist/aun/aid/agentmd.js +66 -2
  13. package/dist/aun/aid/identity.js +4 -1
  14. package/dist/aun/aid/index.js +1 -1
  15. package/dist/aun/msg/group.js +86 -9
  16. package/dist/aun/msg/history.js +213 -36
  17. package/dist/aun/msg/managed-operation.js +58 -9
  18. package/dist/aun/msg/p2p.js +26 -11
  19. package/dist/aun/outbox.js +295 -68
  20. package/dist/aun/service-proxy.js +43 -25
  21. package/dist/channels/aun.js +1004 -273
  22. package/dist/channels/daemon.js +6 -1
  23. package/dist/cli/agent-command.js +4 -3
  24. package/dist/cli/agent.js +66 -56
  25. package/dist/cli/aun-commands.js +177 -42
  26. package/dist/cli/command-log.js +10 -11
  27. package/dist/cli/contact.js +1 -0
  28. package/dist/cli/daemon-commands.js +81 -121
  29. package/dist/cli/index.js +1 -0
  30. package/dist/cli/init.js +76 -24
  31. package/dist/cli/restart-monitor.js +3 -3
  32. package/dist/cli/task-context.js +46 -0
  33. package/dist/cli/trigger-command.js +1 -1
  34. package/dist/cli/watch-logs.js +10 -3
  35. package/dist/config/builtin-roles.js +1 -0
  36. package/dist/config/config-field-policy.js +16 -5
  37. package/dist/config/config-manager.js +226 -24
  38. package/dist/config/config-operation-service.js +1 -2
  39. package/dist/config/contact-operation-service.js +32 -1
  40. package/dist/config/contact-request-service.js +44 -0
  41. package/dist/config/daemon-services.js +186 -0
  42. package/dist/config/gateway-config.js +29 -16
  43. package/dist/config/lifecycle.js +16 -5
  44. package/dist/config/role-service.js +54 -3
  45. package/dist/config/schema-migration.js +550 -0
  46. package/dist/config-store.js +161 -12
  47. package/dist/core/agent-application-service.js +279 -0
  48. package/dist/core/audit/log-integrity.js +102 -0
  49. package/dist/core/auth/agent-delegation.js +31 -1
  50. package/dist/core/auth/auth-gateway.js +33 -4
  51. package/dist/core/auth/authorization-audit.js +155 -4
  52. package/dist/core/auth/operation-authorizer.js +41 -1
  53. package/dist/core/auth/operation-catalog.js +9 -1
  54. package/dist/core/bootstrap-messages.js +2 -2
  55. package/dist/core/bootstrap-service.js +27 -38
  56. package/dist/core/causation/aun-association.js +7 -4
  57. package/dist/core/channel-loader.js +0 -2
  58. package/dist/core/command/agent-control.js +56 -16
  59. package/dist/core/command/command-handler.js +290 -44
  60. package/dist/core/command/connect-menu.js +3 -4
  61. package/dist/core/command/group-menu.js +5 -7
  62. package/dist/core/command/menu-handler.js +279 -80
  63. package/dist/core/command/role-menu.js +21 -11
  64. package/dist/core/command/slash-gate.js +85 -18
  65. package/dist/core/command/slash-handler.js +350 -32
  66. package/dist/core/event-catalog.js +5 -0
  67. package/dist/core/evolagent.js +9 -4
  68. package/dist/core/handoff/dispatcher.js +4 -0
  69. package/dist/core/handoff/runtime.js +10 -0
  70. package/dist/core/handoff/store.js +32 -9
  71. package/dist/core/inference/text-inference.js +7 -15
  72. package/dist/core/message/im-renderer.js +83 -84
  73. package/dist/core/message/{inbound-admission.js → message-admission.js} +51 -1
  74. package/dist/core/message/message-bridge.js +124 -10
  75. package/dist/core/message/message-log.js +14 -7
  76. package/dist/core/message/message-queue.js +206 -16
  77. package/dist/core/message/message-utils.js +12 -5
  78. package/dist/core/message/response-engine.js +495 -72
  79. package/dist/core/message/send-receipt.js +1 -0
  80. package/dist/core/message/stream-debouncer.js +9 -2
  81. package/dist/core/model/model-catalog.js +23 -15
  82. package/dist/core/model/model-diagnostics.js +28 -10
  83. package/dist/core/permission/approval-gateway.js +180 -6
  84. package/dist/core/permission/ec-command-parser.js +465 -56
  85. package/dist/core/permission/mode.js +18 -3
  86. package/dist/core/{protected-paths.js → permission/protected-paths.js} +17 -5
  87. package/dist/core/permission/readonly-shell-query.js +263 -9
  88. package/dist/core/permission/sandbox-runtime.js +205 -13
  89. package/dist/core/permission/tool-policy.js +673 -62
  90. package/dist/core/relation/peer-identity.js +18 -0
  91. package/dist/core/session/session-fs-store.js +154 -5
  92. package/dist/core/session/session-manager.js +299 -30
  93. package/dist/core/session/session-renew.js +19 -12
  94. package/dist/core/session/session-turn-coordinator.js +11 -4
  95. package/dist/eck/kit-renderer.js +18 -9
  96. package/dist/index.js +283 -65
  97. package/dist/ipc.js +374 -24
  98. package/dist/paths.js +64 -7
  99. package/dist/response-system/context-builder.js +1 -7
  100. package/dist/trigger/anomaly-store.js +1 -0
  101. package/dist/trigger/feedback.js +56 -5
  102. package/dist/trigger/history.js +79 -4
  103. package/dist/trigger/legacy-session-history.js +2 -2
  104. package/dist/trigger/parser.js +3 -2
  105. package/dist/trigger/validation.js +6 -1
  106. package/dist/utils/atomic-write.js +27 -0
  107. package/dist/utils/ecweb-utils.js +16 -2
  108. package/dist/utils/error-utils.js +4 -1
  109. package/dist/utils/logger.js +21 -2
  110. package/dist/utils/process-tree-stats.js +24 -4
  111. package/dist/utils/process-tree-worker.js +31 -0
  112. package/dist/utils/project-path.js +1 -2
  113. package/dist/utils/stats.js +52 -18
  114. package/dist/utils/welcome.js +2 -2
  115. package/kits/docs/INDEX.md +1 -1
  116. package/kits/docs/evolcore/INDEX.md +1 -1
  117. package/kits/docs/evolcore/contact.md +7 -1
  118. package/kits/docs/evolcore/msg.md +16 -0
  119. package/kits/rules/01-overview.md +1 -1
  120. package/kits/rules/03-identity.md +1 -1
  121. package/kits/rules/05-venue.md +1 -1
  122. package/kits/schemas/_meta.json +10 -5
  123. package/kits/schemas/agent-config.schema.10.json +2 -1
  124. package/kits/schemas/agent-config.schema.11.json +421 -0
  125. package/kits/schemas/daemon.schema.5.json +135 -0
  126. package/kits/schemas/daemon.schema.6.json +131 -0
  127. package/kits/schemas/defaults.schema.5.json +119 -0
  128. package/kits/schemas/migrations/README.md +3 -1
  129. package/kits/schemas/relation-config.schema.8.json +13 -0
  130. package/kits/schemas/role-config.schema.1.json +1 -2
  131. package/kits/schemas/single-session.schema.3.json +32 -0
  132. package/kits/templates/roles/admin.json +1 -0
  133. package/kits/templates/roles/member.json +1 -0
  134. package/kits/templates/roles/visitor.json +1 -0
  135. package/package.json +7 -3
  136. package/skills/eclink/SKILL.md +2 -0
@@ -1,7 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { resolveCommandPath } from '../../utils/cross-platform.js';
4
- import { getExistingHClassMaskTargets, getExistingLClassReadOnlyTargets, isSameOrDescendant, } from '../protected-paths.js';
4
+ import { getExistingHClassMaskTargets, getExistingLClassReadOnlyTargets, isSameOrDescendant, } from './protected-paths.js';
5
5
  import { resolveRoot } from '../../paths.js';
6
6
  let cachedBubblewrapPath;
7
7
  let sandboxInitializationCircuit;
@@ -9,6 +9,7 @@ let sandboxCircuitNoticeAt = 0;
9
9
  export const SANDBOX_INITIALIZATION_FAILED = 'sandbox_initialization_failed';
10
10
  export const SANDBOX_INITIALIZATION_COOLDOWN_MS = 5 * 60 * 1000;
11
11
  export const SANDBOX_CIRCUIT_NOTICE_INTERVAL_MS = 60 * 1000;
12
+ const SANDBOX_DIAGNOSTIC_TARGET_LIMIT = 256;
12
13
  /** Open a process-wide fail-closed circuit after a confirmed host bootstrap failure. */
13
14
  export function recordSandboxInitializationFailure(error, stderr = [], now = Date.now()) {
14
15
  const detail = createSandboxInitializationError(error, stderr).message.slice(0, 512);
@@ -218,6 +219,163 @@ function pushMaskedDirectory(args, candidate) {
218
219
  return;
219
220
  args.push('--tmpfs', candidate);
220
221
  }
222
+ function decodeMountInfoPath(value) {
223
+ // mountinfo escapes whitespace, backslashes, and newlines as octal bytes.
224
+ return value.replace(/\\([0-7]{3})/g, (_match, octal) => {
225
+ const code = Number.parseInt(octal, 8);
226
+ return Number.isNaN(code) ? _match : String.fromCharCode(code);
227
+ });
228
+ }
229
+ function readMountInfoForPaths(paths, priorityPaths = []) {
230
+ if (process.platform !== 'linux' || paths.length === 0)
231
+ return [];
232
+ let contents;
233
+ try {
234
+ contents = fs.readFileSync('/proc/self/mountinfo', 'utf8');
235
+ }
236
+ catch (error) {
237
+ return [`mountinfo unavailable: ${error instanceof Error ? error.message : String(error)}`];
238
+ }
239
+ const wanted = paths.map(candidate => path.resolve(candidate));
240
+ const priority = priorityPaths.map(candidate => path.resolve(candidate));
241
+ const matches = [];
242
+ for (const [order, line] of contents.split(/\r?\n/).filter(Boolean).entries()) {
243
+ const mountPointField = line.split(' - ', 1)[0]?.split(' ')[4];
244
+ if (!mountPointField)
245
+ continue;
246
+ const mountPoint = path.resolve(decodeMountInfoPath(mountPointField));
247
+ if (!wanted.some(target => isSameOrDescendant(target, mountPoint)
248
+ || isSameOrDescendant(mountPoint, target)))
249
+ continue;
250
+ const isPriority = priority.some(target => target === mountPoint);
251
+ const isPriorityAncestor = !isPriority && priority.some(target => isSameOrDescendant(target, mountPoint));
252
+ matches.push({ line, score: isPriority ? 0 : isPriorityAncestor ? 1 : 2, order });
253
+ }
254
+ return matches
255
+ .sort((left, right) => left.score - right.score || left.order - right.order)
256
+ .slice(0, 32)
257
+ .map(match => match.line);
258
+ }
259
+ function snapshotSandboxPath(candidate) {
260
+ const resolved = path.resolve(candidate);
261
+ try {
262
+ const stat = fs.lstatSync(resolved);
263
+ let kind = 'other';
264
+ if (stat.isFile())
265
+ kind = 'file';
266
+ else if (stat.isDirectory())
267
+ kind = 'directory';
268
+ else if (stat.isSymbolicLink())
269
+ kind = 'symlink';
270
+ let realpath;
271
+ try {
272
+ realpath = fs.realpathSync(resolved);
273
+ }
274
+ catch { }
275
+ return {
276
+ path: resolved,
277
+ exists: true,
278
+ kind,
279
+ dev: stat.dev,
280
+ ino: stat.ino,
281
+ mode: stat.mode & 0o7777,
282
+ uid: stat.uid,
283
+ gid: stat.gid,
284
+ nlink: stat.nlink,
285
+ size: stat.size,
286
+ mtimeMs: stat.mtimeMs,
287
+ ctimeMs: stat.ctimeMs,
288
+ ...(realpath ? { realpath } : {}),
289
+ };
290
+ }
291
+ catch (error) {
292
+ const code = error.code;
293
+ return {
294
+ path: resolved,
295
+ exists: false,
296
+ ...(code ? { error: code } : { error: error instanceof Error ? error.message : String(error) }),
297
+ };
298
+ }
299
+ }
300
+ /** Capture the host-side state used to construct an H-class guard. */
301
+ export function collectSandboxGuardDiagnostics(root = resolveRoot()) {
302
+ const absoluteRoot = path.resolve(root);
303
+ const candidates = [
304
+ path.join(absoluteRoot, 'data', 'causation-aun.json'),
305
+ path.join(absoluteRoot, 'data', 'causation-aun.json_'),
306
+ path.join(absoluteRoot, 'data', 'causation-aun.json__'),
307
+ ];
308
+ let targets = [];
309
+ try {
310
+ targets = getExistingHClassMaskTargets(absoluteRoot).map(target => target.path);
311
+ }
312
+ catch { }
313
+ // Keep the causation file and its atomic-write sidecars first. The full H
314
+ // target list can grow with the number of agents and must not turn every
315
+ // launch into an unbounded log payload.
316
+ const paths = [...new Set([...candidates, ...targets])];
317
+ const selectedPaths = paths.slice(0, SANDBOX_DIAGNOSTIC_TARGET_LIMIT);
318
+ return {
319
+ root: absoluteRoot,
320
+ targetCount: paths.length,
321
+ targetsTruncated: paths.length > selectedPaths.length,
322
+ targets: selectedPaths.map(snapshotSandboxPath),
323
+ mountInfo: readMountInfoForPaths(selectedPaths, candidates),
324
+ };
325
+ }
326
+ /** Reduce path and mount diagnostics to metadata suitable for INFO logs. */
327
+ export function summarizeSandboxGuardDiagnostics(diagnostics) {
328
+ let existingTargetCount = 0;
329
+ let missingTargetCount = 0;
330
+ const targetKindCounts = {};
331
+ for (const target of diagnostics.targets) {
332
+ if (!target.exists) {
333
+ missingTargetCount += 1;
334
+ continue;
335
+ }
336
+ existingTargetCount += 1;
337
+ const kind = target.kind ?? 'unknown';
338
+ targetKindCounts[kind] = (targetKindCounts[kind] ?? 0) + 1;
339
+ }
340
+ return {
341
+ root: diagnostics.root,
342
+ targetCount: diagnostics.targetCount,
343
+ targetsTruncated: diagnostics.targetsTruncated,
344
+ existingTargetCount,
345
+ missingTargetCount,
346
+ targetKindCounts,
347
+ mountInfoCount: diagnostics.mountInfo.length,
348
+ };
349
+ }
350
+ /** Keep the exact mount portion of a bwrap argv while summarizing child argv. */
351
+ export function summarizeBubblewrapArgs(args) {
352
+ const separator = args.indexOf('--');
353
+ const mountArgs = [...args.slice(0, separator >= 0 ? separator : args.length)];
354
+ const child = separator >= 0 ? args.slice(separator + 1) : [];
355
+ return {
356
+ mountArgs,
357
+ ...(child[0] ? { childCommand: child[0] } : {}),
358
+ childArgCount: Math.max(0, child.length - 1),
359
+ };
360
+ }
361
+ /** Keep only argument counts and option names for routine INFO logs. */
362
+ export function summarizeBubblewrapArgsForLog(args) {
363
+ const separator = args.indexOf('--');
364
+ const mountArgs = args.slice(0, separator >= 0 ? separator : args.length);
365
+ const child = separator >= 0 ? args.slice(separator + 1) : [];
366
+ const mountOptionCounts = {};
367
+ for (const arg of mountArgs) {
368
+ if (!arg.startsWith('--'))
369
+ continue;
370
+ mountOptionCounts[arg] = (mountOptionCounts[arg] ?? 0) + 1;
371
+ }
372
+ return {
373
+ mountArgCount: mountArgs.length,
374
+ mountOptionCounts,
375
+ ...(child[0] ? { childCommand: child[0] } : {}),
376
+ childArgCount: Math.max(0, child.length - 1),
377
+ };
378
+ }
221
379
  function appendRuntimeBackedEtcFiles(args) {
222
380
  if (process.platform !== 'linux' || !fs.existsSync('/run'))
223
381
  return;
@@ -285,6 +443,48 @@ function appendLClassReadOnlyBinds(args, root) {
285
443
  args.push('--ro-bind', target.path, target.path);
286
444
  }
287
445
  }
446
+ export function ensureCodexManagedMountpoint(systemConfigDirectory = '/etc/codex', effectiveUid = typeof process.getuid === 'function' ? process.getuid() : undefined) {
447
+ let systemConfigStat;
448
+ try {
449
+ systemConfigStat = fs.lstatSync(systemConfigDirectory);
450
+ }
451
+ catch (error) {
452
+ if (error.code !== 'ENOENT') {
453
+ throw new Error(`Codex managed requirements mountpoint is unavailable: ${systemConfigDirectory} `
454
+ + `could not be inspected (${error instanceof Error ? error.message : String(error)})`);
455
+ }
456
+ }
457
+ if (!systemConfigStat && process.platform === 'linux' && effectiveUid === 0) {
458
+ let created = false;
459
+ try {
460
+ fs.mkdirSync(systemConfigDirectory, { mode: 0o755 });
461
+ created = true;
462
+ }
463
+ catch (error) {
464
+ if (error.code !== 'EEXIST') {
465
+ throw new Error(`Codex managed requirements mountpoint is unavailable: ${systemConfigDirectory} `
466
+ + `could not be created by the root daemon (${error instanceof Error ? error.message : String(error)})`);
467
+ }
468
+ }
469
+ if (created)
470
+ fs.chmodSync(systemConfigDirectory, 0o755);
471
+ try {
472
+ systemConfigStat = fs.lstatSync(systemConfigDirectory);
473
+ }
474
+ catch (error) {
475
+ throw new Error(`Codex managed requirements mountpoint is unavailable: ${systemConfigDirectory} `
476
+ + `could not be verified after creation (${error instanceof Error ? error.message : String(error)})`);
477
+ }
478
+ }
479
+ if (!systemConfigStat) {
480
+ throw new Error(`Codex managed requirements mountpoint is unavailable: ${systemConfigDirectory} `
481
+ + 'must be created as a real directory during installation; run: sudo install -d -m 0755 /etc/codex');
482
+ }
483
+ if (!systemConfigStat.isDirectory() || systemConfigStat.isSymbolicLink()) {
484
+ throw new Error(`Codex managed requirements mountpoint is unsafe: ${systemConfigDirectory} `
485
+ + 'must be a real directory');
486
+ }
487
+ }
288
488
  export function buildHClassGuardCommand(executable, executableArgs, root = resolveRoot(), options = {}) {
289
489
  const bubblewrapPath = resolveBubblewrapPath();
290
490
  if (!bubblewrapPath)
@@ -306,18 +506,10 @@ export function buildHClassGuardCommand(executable, executableArgs, root = resol
306
506
  }
307
507
  const managedDirectory = path.dirname(requirements);
308
508
  const systemConfigDirectory = '/etc/codex';
309
- let systemConfigStat;
310
- try {
311
- systemConfigStat = fs.lstatSync(systemConfigDirectory);
312
- }
313
- catch {
314
- throw new Error(`Codex managed requirements mountpoint is unavailable: ${systemConfigDirectory} `
315
- + 'must be created as a real directory during installation');
316
- }
317
- if (!systemConfigStat.isDirectory() || systemConfigStat.isSymbolicLink()) {
318
- throw new Error(`Codex managed requirements mountpoint is unsafe: ${systemConfigDirectory} `
319
- + 'must be a real directory');
320
- }
509
+ // The npm lifecycle and public installer provision this path. Root-owned
510
+ // legacy daemons may repair only the missing-directory case; unsafe path
511
+ // types and unprivileged processes remain fail-closed.
512
+ ensureCodexManagedMountpoint(systemConfigDirectory);
321
513
  // Keep the copied hook immutable even when a session uses bypass mode.
322
514
  args.push('--ro-bind', managedDirectory, managedDirectory);
323
515
  // The guard starts from a writable root so owner-bypass workspaces keep