evolcore 0.0.12 → 0.0.14

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 (116) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/bin/codex-managed-hook.mjs +4 -1
  3. package/bin/ec-safe-output.js +26 -7
  4. package/bin/install-codex-managed-hooks.mjs +201 -0
  5. package/dist/agents/claude-runner.js +105 -11
  6. package/dist/agents/codex-app-server-client.js +176 -7
  7. package/dist/agents/codex-runner.js +248 -84
  8. package/dist/agents/ecagent-runner.js +21 -2
  9. package/dist/agents/gemini-runner.js +9 -4
  10. package/dist/aun/aid/control-aid.js +36 -13
  11. package/dist/aun/aid/store.js +13 -7
  12. package/dist/aun/group-fs-download.js +56 -0
  13. package/dist/aun/group-identity.js +1 -2
  14. package/dist/aun/msg/group.js +11 -33
  15. package/dist/aun/msg/index.js +1 -2
  16. package/dist/aun/msg/managed-operation.js +176 -55
  17. package/dist/aun/msg/upload.js +16 -1
  18. package/dist/aun/outbox.js +31 -2
  19. package/dist/aun/rpc/client.js +66 -0
  20. package/dist/aun/rpc/index.js +1 -2
  21. package/dist/aun/storage/{upload.js → client.js} +11 -2
  22. package/dist/aun/storage/index.js +1 -2
  23. package/dist/channels/aun.js +514 -150
  24. package/dist/channels/daemon.js +2 -0
  25. package/dist/cli/agent.js +9 -96
  26. package/dist/cli/aun-commands.js +1 -1
  27. package/dist/cli/cli-argv.js +10 -0
  28. package/dist/cli/config.js +9 -17
  29. package/dist/cli/ctl-command.js +1 -1
  30. package/dist/cli/daemon-commands.js +39 -24
  31. package/dist/cli/fs-command.js +65 -19
  32. package/dist/cli/init.js +77 -8
  33. package/dist/cli/queue-command.js +42 -0
  34. package/dist/cli/restart-monitor.js +3 -14
  35. package/dist/cli/task-context.js +172 -0
  36. package/dist/cli/trigger-command.js +7 -0
  37. package/dist/config/access-policy.js +1 -2
  38. package/dist/config/builtin-roles.js +7 -0
  39. package/dist/config/config-manager.js +27 -3
  40. package/dist/config/contact-book-store.js +7 -2
  41. package/dist/config/peer-role-resolver.js +6 -2
  42. package/dist/config/role-migration-startup.js +53 -0
  43. package/dist/config/role-store.js +1 -1
  44. package/dist/core/auth/agent-delegation.js +1 -1
  45. package/dist/core/auth/auth-gateway.js +30 -1
  46. package/dist/core/auth/authorization-audit.js +47 -0
  47. package/dist/core/auth/operation-authorizer.js +1 -0
  48. package/dist/core/auth/operation-catalog.js +10 -1
  49. package/dist/core/bootstrap-messages.js +9 -2
  50. package/dist/core/bootstrap-service.js +8 -1
  51. package/dist/core/command/command-handler.js +201 -51
  52. package/dist/core/command/menu-catalog.js +4 -0
  53. package/dist/core/command/menu-handler.js +85 -18
  54. package/dist/core/command/menu-protocol.js +23 -2
  55. package/dist/core/command/slash-handler.js +58 -10
  56. package/dist/core/event-catalog.js +8 -0
  57. package/dist/core/handoff/runtime.js +12 -1
  58. package/dist/core/handoff/store.js +5 -1
  59. package/dist/core/interaction-router.js +10 -0
  60. package/dist/core/message/im-renderer.js +34 -1
  61. package/dist/core/message/message-bridge.js +68 -13
  62. package/dist/core/message/message-log.js +2 -0
  63. package/dist/core/message/message-queue.js +267 -39
  64. package/dist/core/message/message-utils.js +81 -4
  65. package/dist/core/message/response-engine.js +242 -48
  66. package/dist/core/message/stream-debouncer.js +10 -2
  67. package/dist/core/permission/approval-gateway.js +1 -0
  68. package/dist/core/permission/ec-command-parser.js +649 -18
  69. package/dist/core/permission/index.js +1 -1
  70. package/dist/core/permission/mode.js +15 -0
  71. package/dist/core/permission/readonly-shell-query.js +148 -8
  72. package/dist/core/permission/sandbox-runtime.js +21 -0
  73. package/dist/core/permission/tool-policy.js +266 -56
  74. package/dist/core/protected-paths.js +107 -2
  75. package/dist/core/runtime-lock.js +101 -0
  76. package/dist/core/session/session-manager.js +21 -2
  77. package/dist/core/session/session-mapper.js +20 -1
  78. package/dist/eck/detect.js +18 -1
  79. package/dist/eck/group-rules-sync.js +15 -44
  80. package/dist/index.js +191 -89
  81. package/dist/ipc.js +16 -3
  82. package/dist/paths.js +3 -0
  83. package/dist/response-system/engines/v1/proactive-flow.js +92 -8
  84. package/dist/response-system/modes/single-session/index.js +3 -0
  85. package/dist/trigger/feedback.js +64 -11
  86. package/dist/trigger/history.js +42 -7
  87. package/dist/trigger/manager.js +21 -1
  88. package/dist/trigger/parser.js +14 -2
  89. package/dist/trigger/scheduler.js +1 -0
  90. package/dist/trigger/validation.js +67 -6
  91. package/dist/utils/ecweb-supervisor.js +332 -0
  92. package/dist/utils/error-utils.js +17 -0
  93. package/dist/utils/logger.js +37 -4
  94. package/kits/docs/evolcore/trigger.md +17 -1
  95. package/kits/migrations/migrate-contact-book-v2.mjs +1 -2
  96. package/kits/schemas/role-config.schema.1.json +1 -0
  97. package/kits/templates/roles/admin.json +7 -0
  98. package/kits/templates/roles/member.json +8 -1
  99. package/kits/templates/roles/visitor.json +2 -0
  100. package/package.json +1 -1
  101. package/dist/aun/msg/payload-type.js +0 -27
  102. package/dist/aun/rpc/caller.js +0 -42
  103. package/dist/aun/rpc/connection.js +0 -25
  104. package/dist/aun/storage/manage.js +0 -10
  105. package/dist/config/access-policy-domain.js +0 -18
  106. package/dist/config/config-batch-get.js +0 -11
  107. package/dist/config/owner-policy.js +0 -4
  108. package/dist/config/role-config-v4-startup.js +0 -32
  109. package/dist/config/role-config-v5-startup.js +0 -27
  110. package/dist/core/auth/trigger-authorization.js +0 -15
  111. package/dist/core/interaction-registration.js +0 -10
  112. package/dist/core/permission/execution-sandbox.js +0 -16
  113. package/dist/core/relation/peer-key.js +0 -1
  114. package/dist/core/session/session-key.js +0 -24
  115. package/dist/eck/baseagent-caps.js +0 -18
  116. package/dist/eck/rules-loader.js +0 -28
@@ -2,6 +2,6 @@ export * from './tool-policy.js';
2
2
  export * from './ec-command-parser.js';
3
3
  export * from './readonly-shell-query.js';
4
4
  export * from './approval-gateway.js';
5
- export * from './execution-sandbox.js';
5
+ export * from './mode.js';
6
6
  // Tool summaries live in utils; keep the historical permission import path compatible.
7
7
  export { summarizeToolInput } from '../../utils/tool-summary.js';
@@ -22,3 +22,18 @@ export function normalizePermissionMode(value) {
22
22
  return { mode: 'readonly', workflow: 'plan', migratedFrom: 'plan' };
23
23
  return { mode: 'readonly' };
24
24
  }
25
+ /**
26
+ * Phase one only disables the task execution sandbox for an authenticated
27
+ * owner running in bypass mode. Every other combination remains fail-closed.
28
+ */
29
+ export function resolvePhaseOneExecutionSandbox(permissionMode, role) {
30
+ const normalizedMode = normalizePermissionMode(permissionMode).mode;
31
+ const normalizedRole = role ?? undefined;
32
+ const sandboxOff = normalizedMode === 'bypass' && normalizedRole === 'owner';
33
+ return {
34
+ permissionMode: normalizedMode,
35
+ role: normalizedRole,
36
+ state: sandboxOff ? 'off' : 'active',
37
+ reason: sandboxOff ? 'owner-bypass' : 'phase-one-required',
38
+ };
39
+ }
@@ -3,7 +3,7 @@ import { parseLiteralShellArgv } from './ec-command-parser.js';
3
3
  const MAX_QUERY_LISTS = 8;
4
4
  const MAX_PIPELINE_STAGES = 3;
5
5
  const SUPPORTED_EXECUTABLES = new Set([
6
- 'echo', 'pwd', 'ls', 'cat', 'grep', 'rg', 'find', 'sed', 'head', 'sort',
6
+ 'echo', 'pwd', 'ls', 'cat', 'grep', 'rg', 'find', 'sed', 'head', 'sort', 'git', 'wc', 'stat', 'lstat',
7
7
  ]);
8
8
  const FIND_MUTATING_PRIMARY_RE = /^-(?:delete|exec|execdir|ok|okdir|fprint|fprint0|fprintf|fls)$/;
9
9
  const RG_PROCESS_SPAWNING_OPTION_RE = /^(?:--pre|--hostname-bin|--pre-glob|--search-zip)(?:=|$)/;
@@ -71,8 +71,17 @@ function splitQueryStructure(command) {
71
71
  segment += char;
72
72
  continue;
73
73
  }
74
- if (char === '\0' || char === '\r' || char === '\n' || char === '&' || char === '<')
74
+ if (char === '\0' || char === '\r' || char === '\n' || char === '<')
75
75
  return null;
76
+ if (char === '&') {
77
+ // A bounded sequence of independently proven read-only commands is
78
+ // safe to run in order. A single ampersand would background execution;
79
+ // `||` remains rejected because it introduces conditional control flow.
80
+ if (command[index + 1] !== '&' || !finishPipeline())
81
+ return null;
82
+ index++;
83
+ continue;
84
+ }
76
85
  if (char === '|') {
77
86
  if (command[index + 1] === '|' || !finishStage())
78
87
  return null;
@@ -89,12 +98,14 @@ function splitQueryStructure(command) {
89
98
  return null;
90
99
  return pipelines;
91
100
  }
92
- function parseStageArgv(segment) {
101
+ function parseStageArgv(segment, options) {
93
102
  const redirectMatch = segment.match(STDERR_DEV_NULL_RE);
94
103
  const withoutRedirect = redirectMatch?.[1] ?? segment;
95
104
  if (withoutRedirect.includes('>'))
96
105
  return null;
97
- return parseLiteralShellArgv(withoutRedirect.trim());
106
+ return parseLiteralShellArgv(withoutRedirect.trim(), options.managedTempDir
107
+ ? { allowManagedTmpDir: true, managedTempDir: options.managedTempDir }
108
+ : undefined);
98
109
  }
99
110
  function exact(value) {
100
111
  return { value, access: 'exact' };
@@ -458,12 +469,118 @@ function analyzeSort(args) {
458
469
  }
459
470
  return paths;
460
471
  }
472
+ /**
473
+ * Git metadata queries do not expose file contents and their output paths are
474
+ * controlled by the repository, not by arbitrary shell operands. Keep this
475
+ * deliberately small: status and read-only diff summaries are the patterns
476
+ * used for workspace diagnostics. Mutating subcommands and pathspecs remain
477
+ * unproven so the normal H-class checks still apply.
478
+ */
479
+ function analyzeGit(args) {
480
+ if (args.length === 0)
481
+ return null;
482
+ const subcommand = args[0];
483
+ if (subcommand === 'status') {
484
+ // Keep the allow-list explicit; callers should request a bounded metadata
485
+ // view such as `--short` or `--branch`, rather than inheriting Git's full
486
+ // default output and any future options.
487
+ if (args.length === 1)
488
+ return null;
489
+ const allowed = new Set([
490
+ '--short', '--porcelain', '--porcelain=v1', '--porcelain=v2', '--branch',
491
+ '--untracked-files=no', '--untracked-files=normal', '--ignored=no',
492
+ '--ignore-submodules', '--renames', '--no-renames', '--ahead-behind',
493
+ '--no-ahead-behind', '--show-stash', '--no-optional-locks',
494
+ ]);
495
+ for (const arg of args.slice(1)) {
496
+ if (arg === '--')
497
+ return null;
498
+ if (!allowed.has(arg))
499
+ return null;
500
+ }
501
+ return [];
502
+ }
503
+ if (subcommand === 'diff') {
504
+ let sawSummary = false;
505
+ let optionsEnded = false;
506
+ const paths = [];
507
+ const allowedFlags = new Set([
508
+ '--stat', '--shortstat', '--numstat', '--name-only', '--name-status',
509
+ '--no-ext-diff', '--no-renames', '--no-textconv', '--no-index',
510
+ '--cached', '--staged', '--merge-base', '--no-color', '--color=never',
511
+ ]);
512
+ for (const arg of args.slice(1)) {
513
+ if (!optionsEnded && arg === '--') {
514
+ optionsEnded = true;
515
+ continue;
516
+ }
517
+ if (!optionsEnded && arg.startsWith('-')) {
518
+ if (arg === '--stat' || arg === '--shortstat' || arg === '--numstat'
519
+ || arg === '--name-only' || arg === '--name-status')
520
+ sawSummary = true;
521
+ if (!allowedFlags.has(arg) && !/^[0-9a-f]{7,64}$/.test(arg))
522
+ return null;
523
+ continue;
524
+ }
525
+ // Revisions are safe literals; pathspecs after -- are bounded exact reads.
526
+ if (optionsEnded)
527
+ paths.push({ value: arg, access: 'exact' });
528
+ else if (!/^[0-9A-Za-z._~^/:-]+$/.test(arg))
529
+ return null;
530
+ }
531
+ return sawSummary ? paths : null;
532
+ }
533
+ return null;
534
+ }
535
+ function analyzeWc(args) {
536
+ const allowed = new Set(['-l', '-w', '-c', '-m', '-L', '--lines', '--words', '--bytes', '--chars', '--max-line-length']);
537
+ const paths = [];
538
+ for (const arg of args) {
539
+ if (arg.startsWith('-')) {
540
+ if (!allowed.has(arg))
541
+ return null;
542
+ }
543
+ else if (arg !== '-') {
544
+ paths.push(exact(arg));
545
+ }
546
+ }
547
+ return paths;
548
+ }
549
+ function analyzeStat(args) {
550
+ // GNU stat defaults to lstat semantics. Following a final symlink would
551
+ // let a metadata-only query reach a protected target through an alias.
552
+ if (args.some(argument => argument === '-L' || argument === '--dereference'))
553
+ return null;
554
+ const valueOptions = new Set(['-c', '--format', '--printf']);
555
+ const paths = [];
556
+ let optionsEnded = false;
557
+ for (let index = 0; index < args.length; index++) {
558
+ const argument = args[index];
559
+ if (!optionsEnded && argument === '--') {
560
+ optionsEnded = true;
561
+ continue;
562
+ }
563
+ if (!optionsEnded && argument.startsWith('-') && argument !== '-') {
564
+ const option = argument.split('=', 1)[0];
565
+ if (valueOptions.has(option) && !argument.includes('=') && ++index >= args.length)
566
+ return null;
567
+ if (!valueOptions.has(option) && !new Set(['-f', '--file-system', '-t', '--terse', '--cached']).has(option)) {
568
+ return null;
569
+ }
570
+ continue;
571
+ }
572
+ if (argument !== '-')
573
+ paths.push(exact(argument));
574
+ }
575
+ return paths.length > 0 ? paths : null;
576
+ }
461
577
  function analyzeCommand(argv) {
462
578
  const executable = normalizeExecutable(argv[0]);
463
579
  if (!executable)
464
580
  return null;
465
581
  const args = argv.slice(1);
466
582
  let pathOperands = null;
583
+ let accessKind = 'content';
467
584
  switch (executable) {
468
585
  case 'echo':
469
586
  pathOperands = [];
@@ -495,12 +612,35 @@ function analyzeCommand(argv) {
495
612
  case 'sort':
496
613
  pathOperands = analyzeSort(args);
497
614
  break;
615
+ case 'git':
616
+ pathOperands = analyzeGit(args);
617
+ break;
618
+ case 'wc':
619
+ pathOperands = analyzeWc(args);
620
+ break;
621
+ case 'stat':
622
+ case 'lstat':
623
+ pathOperands = analyzeStat(args);
624
+ accessKind = 'metadata';
625
+ break;
498
626
  }
499
627
  if (!pathOperands)
500
628
  return null;
501
- return { command: { executable, argv, pathOperands } };
629
+ if (['ls', 'find'].includes(executable))
630
+ accessKind = 'enumerate';
631
+ return {
632
+ command: {
633
+ executable,
634
+ argv,
635
+ pathOperands,
636
+ // Keep the established IR shape for ordinary queries. Only metadata
637
+ // commands need an explicit action marker; content/enumeration are
638
+ // derived from their operands by policy consumers.
639
+ ...(accessKind === 'metadata' ? { accessKind } : {}),
640
+ },
641
+ };
502
642
  }
503
- export function analyzeReadonlyShellQuery(command) {
643
+ export function analyzeReadonlyShellQuery(command, options = {}) {
504
644
  const structure = splitQueryStructure(command);
505
645
  if (!structure)
506
646
  return { kind: 'unproven', reason: 'unsupported-shell-structure' };
@@ -509,14 +649,14 @@ export function analyzeReadonlyShellQuery(command) {
509
649
  for (const stages of structure) {
510
650
  const pipeline = [];
511
651
  for (let index = 0; index < stages.length; index++) {
512
- const argv = parseStageArgv(stages[index]);
652
+ const argv = parseStageArgv(stages[index], options);
513
653
  if (!argv)
514
654
  return { kind: 'unproven', reason: 'non-literal-command' };
515
655
  const analyzed = analyzeCommand(argv);
516
656
  if (!analyzed)
517
657
  return { kind: 'unproven', reason: 'unsupported-command-or-option' };
518
658
  if (index > 0) {
519
- if (!['head', 'sort'].includes(analyzed.command.executable)) {
659
+ if (!['head', 'sort', 'grep', 'wc'].includes(analyzed.command.executable)) {
520
660
  return { kind: 'unproven', reason: 'unsupported-pipeline-filter' };
521
661
  }
522
662
  if (analyzed.command.pathOperands.length > 0) {
@@ -4,6 +4,27 @@ import { resolveCommandPath } from '../../utils/cross-platform.js';
4
4
  import { getExistingHClassMaskTargets, getExistingLClassReadOnlyTargets, isSameOrDescendant, } from '../protected-paths.js';
5
5
  import { resolveRoot } from '../../paths.js';
6
6
  let cachedBubblewrapPath;
7
+ export const SANDBOX_INITIALIZATION_FAILED = 'sandbox_initialization_failed';
8
+ /** Recognize sandbox bootstrap failures independently of localized stderr. */
9
+ export function isSandboxInitializationFailure(error, stderr = []) {
10
+ const text = [error instanceof Error ? error.message : String(error ?? ''), ...stderr]
11
+ .join('\n')
12
+ .toLowerCase();
13
+ return text.includes(SANDBOX_INITIALIZATION_FAILED)
14
+ || /(?:apply-seccomp|seccomp)[\s\S]*(?:uid_map|gid_map|operation not permitted)/.test(text)
15
+ || /(?:uid_map|gid_map)[\s\S]*(?:operation not permitted|permission denied)/.test(text)
16
+ || /user namespace[\s\S]*(?:not permitted|unavailable|failed)/.test(text)
17
+ || /bubblewrap[\s\S]*namespace[\s\S]*(?:failed|not permitted|permission denied)/.test(text);
18
+ }
19
+ export function createSandboxInitializationError(error, stderr = []) {
20
+ const detail = error instanceof Error ? error.message : String(error ?? 'sandbox bootstrap failed');
21
+ const wrapped = new Error(`${SANDBOX_INITIALIZATION_FAILED}: ${detail}`);
22
+ wrapped.name = 'SandboxInitializationError';
23
+ wrapped.code = SANDBOX_INITIALIZATION_FAILED;
24
+ if (stderr.length > 0)
25
+ wrapped.stderr = [...stderr].slice(-20);
26
+ return wrapped;
27
+ }
7
28
  /**
8
29
  * Claude Agent SDK's native sandbox is unavailable on Windows. WSL2 reports
9
30
  * itself as Linux, so it remains fail-closed there together with macOS/Linux.