evolcore 0.0.22 → 0.0.24

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 (117) hide show
  1. package/CHANGELOG.md +74 -14
  2. package/README.md +4 -0
  3. package/bin/codex-managed-acl.ps1 +160 -0
  4. package/bin/codex-managed-hook.mjs +127 -10
  5. package/bin/ec-safe-output.js +85 -3
  6. package/bin/install-codex-managed-hooks.mjs +384 -95
  7. package/dist/agents/baseagent-availability.js +48 -0
  8. package/dist/agents/baseagent.js +30 -2
  9. package/dist/agents/claude-runner.js +179 -240
  10. package/dist/agents/{codex-app-server-client.js → codex/app-server-client.js} +90 -20
  11. package/dist/{utils/codex-app-server-registry.js → agents/codex/app-server-registry.js} +2 -2
  12. package/dist/agents/codex/catalog.js +70 -0
  13. package/dist/agents/codex/diagnostics.js +369 -0
  14. package/dist/agents/codex-runner.js +839 -192
  15. package/dist/agents/ecagent-runner.js +27 -34
  16. package/dist/agents/gemini-runner.js +62 -8
  17. package/dist/agents/runner-types.js +1 -1
  18. package/dist/aun/aid/managed-operation.js +28 -6
  19. package/dist/aun/aid/store.js +43 -0
  20. package/dist/aun/menu-client.js +272 -0
  21. package/dist/aun/msg/group.js +65 -4
  22. package/dist/aun/msg/index.js +1 -1
  23. package/dist/aun/msg/managed-operation.js +57 -5
  24. package/dist/aun/outbox.js +56 -18
  25. package/dist/aun/rpc/client.js +7 -1
  26. package/dist/channels/aun.js +178 -77
  27. package/dist/channels/daemon.js +7 -1
  28. package/dist/channels/dingtalk.js +1 -0
  29. package/dist/channels/feishu.js +10 -4
  30. package/dist/channels/wechat.js +81 -10
  31. package/dist/channels/wecom.js +2 -1
  32. package/dist/cli/agent-command.js +29 -3
  33. package/dist/cli/agent-create.js +538 -0
  34. package/dist/cli/agent.js +26 -675
  35. package/dist/cli/aun-commands.js +108 -24
  36. package/dist/cli/command-log.js +21 -2
  37. package/dist/cli/ctl-command.js +2 -0
  38. package/dist/cli/daemon-commands.js +289 -61
  39. package/dist/cli/index.js +22 -2
  40. package/dist/cli/init.js +2 -10
  41. package/dist/cli/managed-command-guard.js +3 -0
  42. package/dist/cli/menu-client.js +95 -0
  43. package/dist/cli/model.js +23 -8
  44. package/dist/cli/restart-monitor.js +432 -8
  45. package/dist/cli/stats.js +110 -0
  46. package/dist/cli/task-context.js +12 -2
  47. package/dist/cli/trigger-command.js +9 -1
  48. package/dist/config/builtin-roles.js +2 -0
  49. package/dist/config/schema-migration.js +14 -2
  50. package/dist/core/auth/agent-delegation.js +10 -7
  51. package/dist/core/auth/auth-gateway.js +3 -2
  52. package/dist/core/auth/authorization-audit.js +75 -18
  53. package/dist/core/auth/operation-authorizer.js +63 -3
  54. package/dist/core/auth/operation-catalog.js +14 -2
  55. package/dist/core/auth/remote-access-store.js +136 -0
  56. package/dist/core/baseagent-loader.js +4 -4
  57. package/dist/core/bootstrap-messages.js +39 -18
  58. package/dist/core/bootstrap-service.js +32 -26
  59. package/dist/core/capability/capability-manager.js +8 -4
  60. package/dist/core/capability/providers/codex-capability-provider.js +1 -1
  61. package/dist/core/command/agent-control.js +7 -2
  62. package/dist/core/command/command-handler.js +128 -30
  63. package/dist/core/command/daemon-diagnostics.js +448 -0
  64. package/dist/core/command/menu-catalog.js +6 -1
  65. package/dist/core/command/menu-handler.js +410 -98
  66. package/dist/core/command/menu-protocol.js +123 -0
  67. package/dist/core/command/slash-gate.js +4 -6
  68. package/dist/core/command/slash-handler.js +199 -70
  69. package/dist/core/data-migration.js +5 -15
  70. package/dist/core/event-catalog.js +3 -0
  71. package/dist/core/handoff/runtime.js +3 -1
  72. package/dist/core/handoff/store.js +7 -1
  73. package/dist/core/message/im-renderer.js +13 -6
  74. package/dist/core/message/message-bridge.js +5 -36
  75. package/dist/core/message/message-queue.js +20 -15
  76. package/dist/core/message/message-utils.js +6 -3
  77. package/dist/core/message/response-engine.js +147 -94
  78. package/dist/core/model/catalog-service.js +310 -0
  79. package/dist/core/model/model-catalog.js +134 -334
  80. package/dist/core/model/model-diagnostics.js +4 -5
  81. package/dist/core/model/model-fallback.js +9 -16
  82. package/dist/core/model/model-permission.js +5 -2
  83. package/dist/core/permission/approval-gateway.js +183 -32
  84. package/dist/core/permission/protected-paths.js +63 -4
  85. package/dist/core/permission/sandbox-runtime.js +4 -2
  86. package/dist/core/permission/tool-policy.js +257 -31
  87. package/dist/core/runtime-lock.js +13 -8
  88. package/dist/core/session/adapters/codex-session-file-adapter.js +71 -3
  89. package/dist/core/session/session-manager.js +49 -13
  90. package/dist/core/session/session-renew.js +1 -0
  91. package/dist/index.js +214 -57
  92. package/dist/ipc.js +191 -102
  93. package/dist/paths.js +23 -1
  94. package/dist/stats/billing.js +18 -4
  95. package/dist/trigger/manager.js +3 -1
  96. package/dist/trigger/parser.js +77 -13
  97. package/dist/trigger/patch.js +104 -1
  98. package/dist/utils/atomic-write.js +26 -2
  99. package/dist/utils/cross-platform.js +436 -1
  100. package/dist/utils/ecweb-supervisor.js +11 -2
  101. package/dist/utils/ecweb-utils.js +8 -26
  102. package/dist/utils/error-utils.js +16 -2
  103. package/dist/utils/model-prices.jsonl +23 -20
  104. package/dist/utils/npm-ops.js +70 -15
  105. package/dist/utils/windows-autostart.js +1 -1
  106. package/kits/docs/evolcore/INDEX.md +1 -0
  107. package/kits/docs/evolcore/agent-create.md +13 -0
  108. package/kits/docs/evolcore/group-collaboration.md +7 -2
  109. package/kits/docs/evolcore/menu-client.md +34 -0
  110. package/kits/migrations/migrate-contact-book-v2.mjs +9 -1
  111. package/kits/migrations/migrate-role-config-v4.mjs +11 -5
  112. package/kits/migrations/migrate-role-config-v5.mjs +8 -2
  113. package/kits/templates/roles/admin.json +2 -0
  114. package/kits/templates/roles/visitor.json +1 -0
  115. package/kits/templates/system-fragments/bootstrap.md +8 -1
  116. package/package.json +16 -4
  117. package/dist/utils/codex-cli.js +0 -43
@@ -3,12 +3,12 @@ import { hasTrustedPrincipal } from '../auth/authenticated-actor.js';
3
3
  import { renderActionAsText } from '../interaction-router.js';
4
4
  import { buildEnvelope, isInteractionSendAccepted, sendInteractionPayload } from '../message/message-utils.js';
5
5
  import { logger } from '../../utils/logger.js';
6
- import { summarizeToolInput } from '../../utils/tool-summary.js';
6
+ import { summarizeToolInput, summarizeToolInputForAudit } from '../../utils/tool-summary.js';
7
7
  import { createRootCausation, deriveCausation, normalizeCausation } from '../causation/context.js';
8
8
  import { recordCausationSpan } from '../causation/audit.js';
9
9
  import { checkDangerousCommand } from './tool-policy.js';
10
10
  import { resolveProtectedCandidate } from './protected-paths.js';
11
- import { auditPermissionDecision } from '../auth/authorization-audit.js';
11
+ import { auditPermissionDecision, auditPermissionLifecycle } from '../auth/authorization-audit.js';
12
12
  export async function requestDangerousCommandPermission(gateway, sessionId, toolName, input, sendPrompt, context, grantScope = 'default', mode = 'request') {
13
13
  const dangerCheck = checkDangerousCommand(toolName, input);
14
14
  if (!dangerCheck.isDangerous) {
@@ -31,6 +31,7 @@ export async function requestDangerousCommandPermission(gateway, sessionId, tool
31
31
  agentName: context?.agentName,
32
32
  role: context?.role,
33
33
  taskId: context?.taskId,
34
+ summary: summarizeToolInputForAudit(toolName, input).slice(0, 512),
34
35
  });
35
36
  return { matched: true, decision: 'deny' };
36
37
  }
@@ -132,6 +133,7 @@ function truncateApprovalDetail(value) {
132
133
  ? `${trimmed.slice(0, APPROVAL_DETAIL_LIMIT - 3)}...`
133
134
  : trimmed;
134
135
  }
136
+ /** Keep unattended execution anomalies useful without copying command values. */
135
137
  function structuredApprovalAuditSummary(input) {
136
138
  const fields = [...new Set(Object.keys(input)
137
139
  .map(key => key.replace(/[^A-Za-z0-9_.-]/g, '_').slice(0, 48))
@@ -446,21 +448,40 @@ export class PermissionGateway {
446
448
  if (sessionGrant)
447
449
  this.addTemporaryGrant(pending, decisionCausation);
448
450
  pending.resolve(decision);
449
- if (!approved) {
450
- auditPermissionDecision({
451
- requestId,
452
- sessionId,
453
- toolName: pending.toolName,
454
- decisionSource: 'approval',
455
- policyCode: 'user_approval_denied',
456
- reason: 'owner approval denied',
457
- permissionMode: pending.permissionMode,
458
- agentAid: pending.selfAid,
459
- agentName: pending.agentName,
460
- role: pending.role,
461
- taskId: pending.taskId,
462
- });
463
- }
451
+ auditPermissionLifecycle({
452
+ phase: 'response',
453
+ requestId,
454
+ sessionId,
455
+ toolName: pending.toolName,
456
+ decision: approved ? 'allow' : 'deny',
457
+ decisionSource: 'approval',
458
+ policyCode: approved ? undefined : 'user_approval_denied',
459
+ reason: approved ? 'owner approval accepted' : 'owner approval denied',
460
+ permissionMode: pending.permissionMode,
461
+ agentAid: pending.selfAid,
462
+ agentName: pending.agentName,
463
+ role: pending.role,
464
+ taskId: pending.taskId,
465
+ summary: pending.auditSummary,
466
+ response: action,
467
+ });
468
+ auditPermissionLifecycle({
469
+ phase: 'final',
470
+ requestId,
471
+ sessionId,
472
+ toolName: pending.toolName,
473
+ decision: approved ? 'allow' : 'deny',
474
+ decisionSource: 'approval',
475
+ policyCode: approved ? undefined : 'user_approval_denied',
476
+ reason: approved ? 'owner approval accepted' : 'owner approval denied',
477
+ permissionMode: pending.permissionMode,
478
+ agentAid: pending.selfAid,
479
+ agentName: pending.agentName,
480
+ role: pending.role,
481
+ taskId: pending.taskId,
482
+ summary: pending.auditSummary,
483
+ response: action,
484
+ });
464
485
  void this.notifyPermissionState(pending, approved
465
486
  ? { state: 'approved', requestId, toolName: pending.toolName }
466
487
  : {
@@ -499,6 +520,23 @@ export class PermissionGateway {
499
520
  pending.resolve('deny');
500
521
  const decisionSource = 'infrastructure';
501
522
  const policyCode = cancelled ? 'approval_cancelled' : `approval_${reason}`;
523
+ auditPermissionLifecycle({
524
+ phase: 'response',
525
+ requestId,
526
+ sessionId,
527
+ toolName: pending.toolName,
528
+ decision: 'deny',
529
+ decisionSource,
530
+ policyCode,
531
+ reason,
532
+ permissionMode: pending.permissionMode,
533
+ agentAid: pending.selfAid,
534
+ agentName: pending.agentName,
535
+ role: pending.role,
536
+ taskId: pending.taskId,
537
+ summary: pending.auditSummary,
538
+ response: reason,
539
+ });
502
540
  auditPermissionDecision({
503
541
  requestId,
504
542
  sessionId,
@@ -511,6 +549,7 @@ export class PermissionGateway {
511
549
  agentName: pending.agentName,
512
550
  role: pending.role,
513
551
  taskId: pending.taskId,
552
+ summary: pending.auditSummary,
514
553
  });
515
554
  if (notifyState) {
516
555
  const failure = permissionFailure(reason);
@@ -558,9 +597,28 @@ export class PermissionGateway {
558
597
  async requestCrossSessionPermission(sessionId, challenge, route, sendPrompt, context, grantScope, requestCausation, grantMatch) {
559
598
  const approval = context.approvalRouting;
560
599
  const interactionRouter = context.interactionRouter;
600
+ const auditSummary = summarizeToolInputForAudit(challenge.toolName, challenge.toolInput).slice(0, 512);
561
601
  if (!approval || !interactionRouter) {
562
602
  await sendPrompt('当前会话权限不足,且没有可用的 owner 审批通道。');
603
+ auditPermissionLifecycle({
604
+ phase: 'response',
605
+ requestId: challenge.id,
606
+ sessionId,
607
+ toolName: challenge.toolName,
608
+ decision: 'deny',
609
+ decisionSource: 'infrastructure',
610
+ policyCode: 'approval_route_unavailable',
611
+ reason: 'owner approval routing context is unavailable',
612
+ permissionMode: context.permissionMode,
613
+ agentAid: context.selfAid,
614
+ agentName: context.agentName,
615
+ role: context.role,
616
+ taskId: context.taskId,
617
+ summary: auditSummary,
618
+ response: 'unavailable',
619
+ });
563
620
  auditPermissionDecision({
621
+ requestId: challenge.id,
564
622
  sessionId,
565
623
  toolName: challenge.toolName,
566
624
  decisionSource: 'infrastructure',
@@ -571,6 +629,7 @@ export class PermissionGateway {
571
629
  agentName: context.agentName,
572
630
  role: context.role,
573
631
  taskId: context.taskId,
632
+ summary: structuredApprovalAuditSummary(challenge.toolInput),
574
633
  });
575
634
  return 'deny';
576
635
  }
@@ -673,6 +732,7 @@ export class PermissionGateway {
673
732
  interactionRouter,
674
733
  causation: requestCausation,
675
734
  permissionStateChanged: context.permissionStateChanged,
735
+ auditSummary,
676
736
  };
677
737
  this.crossSessionPending.set(requestId, pending);
678
738
  this.syncManagementRevalidationTimer();
@@ -776,6 +836,23 @@ export class PermissionGateway {
776
836
  this.syncManagementRevalidationTimer();
777
837
  pending.resolve('deny');
778
838
  const decisionSource = reason === 'denied' ? 'approval' : 'infrastructure';
839
+ auditPermissionLifecycle({
840
+ phase: 'response',
841
+ requestId,
842
+ sessionId,
843
+ toolName: pending.toolName,
844
+ decision: 'deny',
845
+ decisionSource,
846
+ policyCode: `approval_${reason}`,
847
+ reason,
848
+ permissionMode: pending.permissionMode,
849
+ agentAid: pending.selfAid,
850
+ agentName: pending.agentName,
851
+ role: pending.role,
852
+ taskId: pending.taskId,
853
+ summary: pending.auditSummary,
854
+ response: reason,
855
+ });
779
856
  auditPermissionDecision({
780
857
  requestId,
781
858
  sessionId,
@@ -788,6 +865,7 @@ export class PermissionGateway {
788
865
  agentName: pending.agentName,
789
866
  role: pending.role,
790
867
  taskId: pending.taskId,
868
+ summary: pending.auditSummary,
791
869
  });
792
870
  const decisionCausation = deriveCausation(pending.causation);
793
871
  recordCausationSpan(decisionCausation, 'permission.decision', {
@@ -815,6 +893,7 @@ export class PermissionGateway {
815
893
  const effectiveApproverPolicy = approverPolicy
816
894
  ?? context?.approvalRouting?.approverPolicy
817
895
  ?? 'requester';
896
+ const auditSummary = summarizeToolInputForAudit(toolName, toolInput).slice(0, 512);
818
897
  if (context?.approvalInteractionPolicy === 'deny') {
819
898
  const denialRequestId = `perm-policy-${randomUUID()}`;
820
899
  const anomaly = {
@@ -852,6 +931,7 @@ export class PermissionGateway {
852
931
  agentName: context.agentName,
853
932
  role: context.role,
854
933
  taskId: context.taskId,
934
+ summary: auditSummary,
855
935
  });
856
936
  return 'deny';
857
937
  }
@@ -868,6 +948,7 @@ export class PermissionGateway {
868
948
  agentName: context?.agentName,
869
949
  role: context?.role,
870
950
  taskId: context?.taskId,
951
+ summary: auditSummary,
871
952
  });
872
953
  return 'deny';
873
954
  }
@@ -904,6 +985,20 @@ export class PermissionGateway {
904
985
  refs: { permissionRequestId: requestId, taskId: context.taskId, sessionId },
905
986
  });
906
987
  this.eventBus?.publish({ type: 'permission:requested', sessionId, requestId, toolName, input: displaySummary, causation: requestCausation });
988
+ auditPermissionLifecycle({
989
+ phase: 'request',
990
+ requestId,
991
+ sessionId,
992
+ toolName,
993
+ decision: 'pending',
994
+ decisionSource: 'approval',
995
+ permissionMode: context?.permissionMode,
996
+ agentAid: context?.selfAid,
997
+ agentName: context?.agentName,
998
+ role: context?.role,
999
+ taskId: context?.taskId,
1000
+ summary: auditSummary,
1001
+ });
907
1002
  const route = planApprovalRoute(challenge, context);
908
1003
  logger.info(`[PermissionGateway] approval_route request=${requestId} policy=${challenge.approverPolicy}`
909
1004
  + ` route=${route.kind}`
@@ -942,6 +1037,24 @@ export class PermissionGateway {
942
1037
  agentName: context.agentName,
943
1038
  role: context.role,
944
1039
  taskId: context.taskId,
1040
+ summary: summarizeToolInputForAudit(toolName, toolInput).slice(0, 512),
1041
+ });
1042
+ auditPermissionLifecycle({
1043
+ phase: 'response',
1044
+ requestId,
1045
+ sessionId,
1046
+ toolName,
1047
+ decision: 'deny',
1048
+ decisionSource: 'infrastructure',
1049
+ policyCode: 'approval_route_unavailable',
1050
+ reason: route.reason,
1051
+ permissionMode: context.permissionMode,
1052
+ agentAid: context.selfAid,
1053
+ agentName: context.agentName,
1054
+ role: context.role,
1055
+ taskId: context.taskId,
1056
+ summary: summarizeToolInputForAudit(toolName, toolInput).slice(0, 512),
1057
+ response: 'unavailable',
945
1058
  });
946
1059
  return 'deny';
947
1060
  }
@@ -1006,6 +1119,7 @@ export class PermissionGateway {
1006
1119
  permissionMode: context?.permissionMode,
1007
1120
  role: context?.role,
1008
1121
  taskId: context?.taskId,
1122
+ auditSummary,
1009
1123
  };
1010
1124
  pending.timeout = setTimeout(() => {
1011
1125
  this.failPendingPermission(sessionId, requestId, 'expired');
@@ -1112,6 +1226,40 @@ export class PermissionGateway {
1112
1226
  pending.resolve(normalizedDecision === 'deny' ? 'deny' : 'allow');
1113
1227
  this.pending.delete(requestId);
1114
1228
  this.syncManagementRevalidationTimer();
1229
+ auditPermissionLifecycle({
1230
+ phase: 'response',
1231
+ requestId,
1232
+ sessionId,
1233
+ toolName: pending.toolName,
1234
+ decision: normalizedDecision === 'deny' ? 'deny' : 'allow',
1235
+ decisionSource: 'approval',
1236
+ policyCode: normalizedDecision === 'deny' ? 'user_approval_denied' : undefined,
1237
+ reason: normalizedDecision === 'deny' ? 'user approval denied' : 'user approval accepted',
1238
+ permissionMode: pending.permissionMode,
1239
+ agentAid: pending.selfAid,
1240
+ agentName: pending.agentName,
1241
+ role: pending.role,
1242
+ taskId: pending.taskId,
1243
+ summary: pending.auditSummary,
1244
+ response: normalizedDecision,
1245
+ });
1246
+ auditPermissionLifecycle({
1247
+ phase: 'final',
1248
+ requestId,
1249
+ sessionId,
1250
+ toolName: pending.toolName,
1251
+ decision: normalizedDecision === 'deny' ? 'deny' : 'allow',
1252
+ decisionSource: 'approval',
1253
+ policyCode: normalizedDecision === 'deny' ? 'user_approval_denied' : undefined,
1254
+ reason: normalizedDecision === 'deny' ? 'user approval denied' : 'user approval accepted',
1255
+ permissionMode: pending.permissionMode,
1256
+ agentAid: pending.selfAid,
1257
+ agentName: pending.agentName,
1258
+ role: pending.role,
1259
+ taskId: pending.taskId,
1260
+ summary: pending.auditSummary,
1261
+ response: normalizedDecision,
1262
+ });
1115
1263
  const decisionCausation = deriveCausation(pending.causation);
1116
1264
  if (normalizedDecision === 'always') {
1117
1265
  this.addTemporaryGrant(pending, decisionCausation);
@@ -1123,21 +1271,6 @@ export class PermissionGateway {
1123
1271
  });
1124
1272
  const decisionSource = 'approval';
1125
1273
  const policyCode = normalizedDecision === 'deny' ? 'user_approval_denied' : undefined;
1126
- if (normalizedDecision === 'deny') {
1127
- auditPermissionDecision({
1128
- requestId,
1129
- sessionId,
1130
- toolName: pending.toolName,
1131
- decisionSource,
1132
- policyCode: 'user_approval_denied',
1133
- reason: 'user approval denied',
1134
- permissionMode: pending.permissionMode,
1135
- agentAid: pending.selfAid,
1136
- agentName: pending.agentName,
1137
- role: pending.role,
1138
- taskId: pending.taskId,
1139
- });
1140
- }
1141
1274
  this.eventBus?.publish({ type: 'permission:resolved', sessionId, requestId, toolName: pending.toolName, approved: normalizedDecision !== 'deny', ...(policyCode ? { decisionSource, policyCode } : { decisionSource }), causation: decisionCausation });
1142
1275
  return true;
1143
1276
  }
@@ -1201,6 +1334,23 @@ export class PermissionGateway {
1201
1334
  this.clearPendingResources(pending);
1202
1335
  pending.interactionRouter?.cancel(requestId, reason);
1203
1336
  pending.resolve('deny');
1337
+ auditPermissionLifecycle({
1338
+ phase: 'response',
1339
+ requestId,
1340
+ sessionId,
1341
+ toolName: pending.toolName,
1342
+ decision: 'deny',
1343
+ decisionSource: 'infrastructure',
1344
+ policyCode: `approval_${reason}`,
1345
+ reason,
1346
+ permissionMode: pending.permissionMode,
1347
+ agentAid: pending.selfAid,
1348
+ agentName: pending.agentName,
1349
+ role: pending.role,
1350
+ taskId: pending.taskId,
1351
+ summary: pending.auditSummary,
1352
+ response: reason,
1353
+ });
1204
1354
  auditPermissionDecision({
1205
1355
  requestId,
1206
1356
  sessionId,
@@ -1213,6 +1363,7 @@ export class PermissionGateway {
1213
1363
  agentName: pending.agentName,
1214
1364
  role: pending.role,
1215
1365
  taskId: pending.taskId,
1366
+ summary: pending.auditSummary,
1216
1367
  });
1217
1368
  this.pending.delete(requestId);
1218
1369
  const decisionCausation = deriveCausation(pending.causation);
@@ -302,6 +302,7 @@ export function getLClassWritePatterns(root = resolveRoot()) {
302
302
  }
303
303
  const CLAUDE_PROTECTED_TARGET_LIMIT = 8192;
304
304
  const CLAUDE_GLOB_MAGIC = /[*?[\]]/;
305
+ const CODEX_GLOB_MAGIC = /[*?[\]]/;
305
306
  function addExistingTarget(targets, value) {
306
307
  try {
307
308
  const stat = fs.lstatSync(value);
@@ -427,12 +428,70 @@ export function buildClaudeProtectedFilesystem(root = resolveRoot(), options = {
427
428
  };
428
429
  }
429
430
  export function buildCodexHClassFilesystemRules(root = resolveRoot()) {
430
- return {
431
+ const filesystem = {
431
432
  // Codex pre-expands unbounded deny globs on Linux/WSL/Windows. Without a
432
433
  // bound, patterns such as **/*.json_ are accepted but may not be enforced.
433
434
  glob_scan_max_depth: 64,
434
- ...Object.fromEntries(getHClassSandboxPatterns(root).map(pattern => [pattern, 'deny'])),
435
+ ...Object.fromEntries(filterWindowsCodexFilesystemTargets(getHClassSandboxPatterns(root)).map(pattern => [pattern, 'deny'])),
435
436
  };
437
+ return compactWindowsCodexFilesystemRules(filesystem);
438
+ }
439
+ /**
440
+ * Windows Codex sandbox setup materializes a path while applying its deny-read
441
+ * ACL. A nonexistent exact file target can therefore become a directory on
442
+ * disk (for example `daemon.pid`). Keep existing paths and globs, but omit
443
+ * nonexistent exact paths so the sandbox never creates the wrong filesystem
444
+ * object. Non-Windows callers retain the historical rule set.
445
+ */
446
+ export function filterWindowsCodexFilesystemTargets(patterns, platform = process.platform) {
447
+ if (platform !== 'win32')
448
+ return patterns;
449
+ return patterns.filter(pattern => {
450
+ if (CODEX_GLOB_MAGIC.test(pattern))
451
+ return true;
452
+ try {
453
+ fs.lstatSync(pattern);
454
+ return true;
455
+ }
456
+ catch (error) {
457
+ const code = error && typeof error === 'object' && 'code' in error
458
+ ? error.code
459
+ : undefined;
460
+ return code === 'ENOENT' ? false : true;
461
+ }
462
+ });
463
+ }
464
+ /**
465
+ * Codex expands recursive deny globs into individual targets. Large protected
466
+ * trees on Windows can then fail during AGENTS.md loading with os error 206.
467
+ * Directory denies inherit to descendants through the Windows sandbox ACLs,
468
+ * avoiding redundant targets. Keep broad recursive filename globs intact.
469
+ */
470
+ export function compactWindowsCodexFilesystemRules(filesystem, platform = process.platform) {
471
+ if (platform !== 'win32')
472
+ return filesystem;
473
+ const compacted = {};
474
+ for (const [pattern, access] of Object.entries(filesystem)) {
475
+ const isTerminalRecursiveDeny = access === 'deny'
476
+ && (pattern.endsWith('/**') || pattern.endsWith('\\**'));
477
+ const recursiveRoot = isTerminalRecursiveDeny ? pattern.slice(0, -3) : undefined;
478
+ // Only collapse a recursive deny when its root is a real directory. A
479
+ // missing root must remain a glob: turning it into an exact target can
480
+ // make the Windows sandbox materialize that path as a directory.
481
+ const canCompact = !!recursiveRoot
482
+ && (CODEX_GLOB_MAGIC.test(recursiveRoot) || (() => {
483
+ try {
484
+ return fs.lstatSync(recursiveRoot).isDirectory();
485
+ }
486
+ catch {
487
+ return false;
488
+ }
489
+ })());
490
+ const compactPattern = canCompact ? recursiveRoot : pattern;
491
+ if (compacted[compactPattern] !== 'deny')
492
+ compacted[compactPattern] = access;
493
+ }
494
+ return compacted;
436
495
  }
437
496
  export function buildCodexProtectedFilesystemRules(root = resolveRoot(), options = {}) {
438
497
  const lClassRules = {};
@@ -459,10 +518,10 @@ export function buildCodexProtectedFilesystemRules(root = resolveRoot(), options
459
518
  lClassRules[target.path] = lClassAccess;
460
519
  }
461
520
  }
462
- return {
521
+ return compactWindowsCodexFilesystemRules({
463
522
  ...buildCodexHClassFilesystemRules(root),
464
523
  ...lClassRules,
465
- };
524
+ });
466
525
  }
467
526
  export function hClassGrantIncludesProtectedPath(value, options = {}) {
468
527
  const root = resolveThroughExistingAncestor(options.root ?? resolveRoot());
@@ -497,8 +497,10 @@ export function buildHClassGuardCommand(executable, executableArgs, root = resol
497
497
  '--dev-bind', '/dev', '/dev',
498
498
  '--proc', '/proc',
499
499
  ];
500
- appendLClassReadOnlyBinds(args, path.resolve(root));
501
- appendHClassMasks(args, path.resolve(root), true);
500
+ if (options.protectEvolcoreFilesystem !== false) {
501
+ appendLClassReadOnlyBinds(args, path.resolve(root));
502
+ appendHClassMasks(args, path.resolve(root), true);
503
+ }
502
504
  if (options.managedCodexRequirements) {
503
505
  const requirements = path.resolve(options.managedCodexRequirements);
504
506
  if (!fs.statSync(requirements).isFile()) {