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.
- package/CHANGELOG.md +74 -14
- package/README.md +4 -0
- package/bin/codex-managed-acl.ps1 +160 -0
- package/bin/codex-managed-hook.mjs +127 -10
- package/bin/ec-safe-output.js +85 -3
- package/bin/install-codex-managed-hooks.mjs +384 -95
- package/dist/agents/baseagent-availability.js +48 -0
- package/dist/agents/baseagent.js +30 -2
- package/dist/agents/claude-runner.js +179 -240
- package/dist/agents/{codex-app-server-client.js → codex/app-server-client.js} +90 -20
- package/dist/{utils/codex-app-server-registry.js → agents/codex/app-server-registry.js} +2 -2
- package/dist/agents/codex/catalog.js +70 -0
- package/dist/agents/codex/diagnostics.js +369 -0
- package/dist/agents/codex-runner.js +839 -192
- package/dist/agents/ecagent-runner.js +27 -34
- package/dist/agents/gemini-runner.js +62 -8
- package/dist/agents/runner-types.js +1 -1
- package/dist/aun/aid/managed-operation.js +28 -6
- package/dist/aun/aid/store.js +43 -0
- package/dist/aun/menu-client.js +272 -0
- package/dist/aun/msg/group.js +65 -4
- package/dist/aun/msg/index.js +1 -1
- package/dist/aun/msg/managed-operation.js +57 -5
- package/dist/aun/outbox.js +56 -18
- package/dist/aun/rpc/client.js +7 -1
- package/dist/channels/aun.js +178 -77
- package/dist/channels/daemon.js +7 -1
- package/dist/channels/dingtalk.js +1 -0
- package/dist/channels/feishu.js +10 -4
- package/dist/channels/wechat.js +81 -10
- package/dist/channels/wecom.js +2 -1
- package/dist/cli/agent-command.js +29 -3
- package/dist/cli/agent-create.js +538 -0
- package/dist/cli/agent.js +26 -675
- package/dist/cli/aun-commands.js +108 -24
- package/dist/cli/command-log.js +21 -2
- package/dist/cli/ctl-command.js +2 -0
- package/dist/cli/daemon-commands.js +289 -61
- package/dist/cli/index.js +22 -2
- package/dist/cli/init.js +2 -10
- package/dist/cli/managed-command-guard.js +3 -0
- package/dist/cli/menu-client.js +95 -0
- package/dist/cli/model.js +23 -8
- package/dist/cli/restart-monitor.js +432 -8
- package/dist/cli/stats.js +110 -0
- package/dist/cli/task-context.js +12 -2
- package/dist/cli/trigger-command.js +9 -1
- package/dist/config/builtin-roles.js +2 -0
- package/dist/config/schema-migration.js +14 -2
- package/dist/core/auth/agent-delegation.js +10 -7
- package/dist/core/auth/auth-gateway.js +3 -2
- package/dist/core/auth/authorization-audit.js +75 -18
- package/dist/core/auth/operation-authorizer.js +63 -3
- package/dist/core/auth/operation-catalog.js +14 -2
- package/dist/core/auth/remote-access-store.js +136 -0
- package/dist/core/baseagent-loader.js +4 -4
- package/dist/core/bootstrap-messages.js +39 -18
- package/dist/core/bootstrap-service.js +32 -26
- package/dist/core/capability/capability-manager.js +8 -4
- package/dist/core/capability/providers/codex-capability-provider.js +1 -1
- package/dist/core/command/agent-control.js +7 -2
- package/dist/core/command/command-handler.js +128 -30
- package/dist/core/command/daemon-diagnostics.js +448 -0
- package/dist/core/command/menu-catalog.js +6 -1
- package/dist/core/command/menu-handler.js +410 -98
- package/dist/core/command/menu-protocol.js +123 -0
- package/dist/core/command/slash-gate.js +4 -6
- package/dist/core/command/slash-handler.js +199 -70
- package/dist/core/data-migration.js +5 -15
- package/dist/core/event-catalog.js +3 -0
- package/dist/core/handoff/runtime.js +3 -1
- package/dist/core/handoff/store.js +7 -1
- package/dist/core/message/im-renderer.js +13 -6
- package/dist/core/message/message-bridge.js +5 -36
- package/dist/core/message/message-queue.js +20 -15
- package/dist/core/message/message-utils.js +6 -3
- package/dist/core/message/response-engine.js +147 -94
- package/dist/core/model/catalog-service.js +310 -0
- package/dist/core/model/model-catalog.js +134 -334
- package/dist/core/model/model-diagnostics.js +4 -5
- package/dist/core/model/model-fallback.js +9 -16
- package/dist/core/model/model-permission.js +5 -2
- package/dist/core/permission/approval-gateway.js +183 -32
- package/dist/core/permission/protected-paths.js +63 -4
- package/dist/core/permission/sandbox-runtime.js +4 -2
- package/dist/core/permission/tool-policy.js +257 -31
- package/dist/core/runtime-lock.js +13 -8
- package/dist/core/session/adapters/codex-session-file-adapter.js +71 -3
- package/dist/core/session/session-manager.js +49 -13
- package/dist/core/session/session-renew.js +1 -0
- package/dist/index.js +214 -57
- package/dist/ipc.js +191 -102
- package/dist/paths.js +23 -1
- package/dist/stats/billing.js +18 -4
- package/dist/trigger/manager.js +3 -1
- package/dist/trigger/parser.js +77 -13
- package/dist/trigger/patch.js +104 -1
- package/dist/utils/atomic-write.js +26 -2
- package/dist/utils/cross-platform.js +436 -1
- package/dist/utils/ecweb-supervisor.js +11 -2
- package/dist/utils/ecweb-utils.js +8 -26
- package/dist/utils/error-utils.js +16 -2
- package/dist/utils/model-prices.jsonl +23 -20
- package/dist/utils/npm-ops.js +70 -15
- package/dist/utils/windows-autostart.js +1 -1
- package/kits/docs/evolcore/INDEX.md +1 -0
- package/kits/docs/evolcore/agent-create.md +13 -0
- package/kits/docs/evolcore/group-collaboration.md +7 -2
- package/kits/docs/evolcore/menu-client.md +34 -0
- package/kits/migrations/migrate-contact-book-v2.mjs +9 -1
- package/kits/migrations/migrate-role-config-v4.mjs +11 -5
- package/kits/migrations/migrate-role-config-v5.mjs +8 -2
- package/kits/templates/roles/admin.json +2 -0
- package/kits/templates/roles/visitor.json +1 -0
- package/kits/templates/system-fragments/bootstrap.md +8 -1
- package/package.json +16 -4
- 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
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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
|
-
|
|
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
|
-
|
|
501
|
-
|
|
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()) {
|