evolcore 0.0.25 → 0.0.26

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 (231) hide show
  1. package/CHANGELOG.md +607 -565
  2. package/LICENSE +21 -21
  3. package/README.md +455 -455
  4. package/bin/codex-managed-acl.ps1 +160 -160
  5. package/bin/codex-managed-hook.mjs +208 -208
  6. package/bin/ec-safe-output.js +327 -327
  7. package/bin/ec.js +40 -29
  8. package/bin/install-codex-managed-hooks.mjs +582 -555
  9. package/dist/agents/baseagent.js +96 -46
  10. package/dist/agents/claude-runner.js +50 -12
  11. package/dist/agents/codex/app-server-client.js +13 -5
  12. package/dist/agents/codex/catalog.js +1 -1
  13. package/dist/agents/codex/diagnostics.js +34 -1
  14. package/dist/agents/codex-runner.js +121 -38
  15. package/dist/agents/ecagent-runner.js +4 -9
  16. package/dist/agents/gemini-runner.js +2 -7
  17. package/dist/agents/runner-types.js +25 -17
  18. package/dist/aun/aid/agentmd.js +81 -8
  19. package/dist/aun/aid/client.js +1 -1
  20. package/dist/aun/aid/identity.js +1 -1
  21. package/dist/aun/aid/index.js +1 -1
  22. package/dist/aun/aid/store.js +17 -13
  23. package/dist/aun/menu-client.js +74 -0
  24. package/dist/aun/msg/group.js +1 -1
  25. package/dist/aun/msg/managed-operation.js +1 -1
  26. package/dist/aun/msg/p2p.js +6 -1
  27. package/dist/aun/msg/upload.js +30 -22
  28. package/dist/channels/aun.js +329 -94
  29. package/dist/cli/agent-command.js +105 -84
  30. package/dist/cli/agent-create.js +112 -10
  31. package/dist/cli/agent.js +16 -5
  32. package/dist/cli/aun-commands.js +297 -296
  33. package/dist/cli/bench.js +20 -20
  34. package/dist/cli/config.js +47 -28
  35. package/dist/cli/contact.js +70 -13
  36. package/dist/cli/ctl-command.js +22 -22
  37. package/dist/cli/daemon-commands.js +105 -40
  38. package/dist/cli/diag.js +31 -0
  39. package/dist/cli/fs-command.js +67 -67
  40. package/dist/cli/handoff-command.js +41 -9
  41. package/dist/cli/index.js +83 -77
  42. package/dist/cli/link-rules.js +21 -21
  43. package/dist/cli/managed-command-guard.js +3 -0
  44. package/dist/cli/menu-client.js +46 -31
  45. package/dist/cli/net-check.js +22 -22
  46. package/dist/cli/queue-command.js +29 -29
  47. package/dist/cli/response.js +30 -30
  48. package/dist/cli/restart-monitor.js +87 -40
  49. package/dist/cli/stats.js +51 -51
  50. package/dist/cli/trigger-command.js +56 -56
  51. package/dist/config/builtin-roles.js +1 -0
  52. package/dist/config/config-operation-service.js +145 -0
  53. package/dist/config/contact-operation-service.js +96 -7
  54. package/dist/config/resolved-config-op.js +27 -7
  55. package/dist/core/agent-application-service.js +2 -1
  56. package/dist/core/auth/authorization-audit.js +20 -12
  57. package/dist/core/auth/operation-authorizer.js +25 -4
  58. package/dist/core/auth/operation-catalog.js +35 -4
  59. package/dist/core/auth/remote-access-store.js +40 -1
  60. package/dist/core/bootstrap-service.js +1 -1
  61. package/dist/core/command/command-handler.js +83 -26
  62. package/dist/core/command/diag-service.js +118 -0
  63. package/dist/core/command/menu-catalog.js +12 -3
  64. package/dist/core/command/menu-handler.js +47 -26
  65. package/dist/core/command/menu-protocol.js +5 -0
  66. package/dist/core/command/menu-token-store.js +41 -8
  67. package/dist/core/command/slash-gate.js +34 -4
  68. package/dist/core/command/slash-handler.js +86 -21
  69. package/dist/core/handoff/runtime.js +205 -27
  70. package/dist/core/handoff/store.js +193 -4
  71. package/dist/core/message/im-renderer.js +72 -21
  72. package/dist/core/message/message-bridge.js +69 -9
  73. package/dist/core/message/message-queue.js +26 -0
  74. package/dist/core/message/message-utils.js +1 -0
  75. package/dist/core/message/response-engine.js +47 -28
  76. package/dist/core/model/catalog-service.js +79 -49
  77. package/dist/core/model/context-window-suffix.js +33 -0
  78. package/dist/core/model/model-catalog.js +1 -1
  79. package/dist/core/model/model-permission.js +12 -3
  80. package/dist/core/permission/ec-command-parser.js +51 -12
  81. package/dist/core/permission/sandbox-runtime.js +36 -14
  82. package/dist/core/permission/tool-policy.js +2 -1
  83. package/dist/index.js +280 -163
  84. package/dist/ipc.js +54 -2
  85. package/dist/response-system/engines/v1/proactive-flow.js +59 -2
  86. package/dist/stats/billing.js +41 -15
  87. package/dist/stats/db.js +159 -159
  88. package/dist/stats/eck-vars.js +9 -9
  89. package/dist/stats/price-resolver.js +28 -10
  90. package/dist/stats/query.js +139 -139
  91. package/dist/stats/role-budget.js +6 -6
  92. package/dist/stats/writer.js +51 -51
  93. package/dist/utils/cross-platform.js +30 -1
  94. package/dist/utils/error-dict.json +160 -160
  95. package/dist/utils/error-utils.js +21 -0
  96. package/dist/utils/evolcore-version.js +129 -0
  97. package/dist/utils/logger.js +3 -1
  98. package/dist/utils/macos-autostart.js +22 -22
  99. package/dist/utils/model-prices.jsonl +23 -23
  100. package/dist/utils/npm-ops.js +176 -35
  101. package/dist/utils/welcome.js +22 -22
  102. package/ecagent/LICENSE +21 -21
  103. package/ecagent/NOTICE +11 -11
  104. package/ecagent/README.md +61 -61
  105. package/ecagent/dist/harness/compaction/branch-summarization.js +30 -30
  106. package/ecagent/dist/harness/compaction/compaction.js +83 -83
  107. package/ecagent/dist/harness/messages.js +7 -7
  108. package/kits/docs/GUIDE.md +20 -20
  109. package/kits/docs/INDEX.md +66 -66
  110. package/kits/docs/aun/CHEATSHEET.md +19 -19
  111. package/kits/docs/aun/SYNC_PROTOCOL.md +15 -15
  112. package/kits/docs/channels/aun.md +56 -56
  113. package/kits/docs/channels/feishu.md +56 -56
  114. package/kits/docs/context-assembly.md +364 -364
  115. package/kits/docs/eck_templates/GUIDE.template.md +22 -22
  116. package/kits/docs/eck_templates/INDEX.template.md +28 -28
  117. package/kits/docs/eck_templates/path-registry.template.md +33 -33
  118. package/kits/docs/eck_templates/runtime.template.md +19 -19
  119. package/kits/docs/evolcore/INDEX.md +71 -71
  120. package/kits/docs/evolcore/agent-create.md +170 -165
  121. package/kits/docs/evolcore/agent.md +66 -66
  122. package/kits/docs/evolcore/aid.md +60 -60
  123. package/kits/docs/evolcore/codex-windows-sandbox.md +146 -146
  124. package/kits/docs/evolcore/config.md +202 -195
  125. package/kits/docs/evolcore/contact.md +75 -68
  126. package/kits/docs/evolcore/ctl.md +46 -46
  127. package/kits/docs/evolcore/event.md +216 -216
  128. package/kits/docs/evolcore/fs-architecture.md +1215 -1215
  129. package/kits/docs/evolcore/fs.md +110 -110
  130. package/kits/docs/evolcore/group-collaboration.md +243 -243
  131. package/kits/docs/evolcore/group-fs.md +17 -17
  132. package/kits/docs/evolcore/group-rules.md +251 -251
  133. package/kits/docs/evolcore/group.md +152 -152
  134. package/kits/docs/evolcore/menu-client.md +42 -34
  135. package/kits/docs/evolcore/model.md +50 -50
  136. package/kits/docs/evolcore/msg.md +166 -165
  137. package/kits/docs/evolcore/response.md +75 -75
  138. package/kits/docs/evolcore/rpc.md +37 -37
  139. package/kits/docs/evolcore/self-summary.md +29 -29
  140. package/kits/docs/evolcore/stats.md +83 -83
  141. package/kits/docs/evolcore/storage.md +50 -50
  142. package/kits/docs/evolcore/trigger.md +575 -575
  143. package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -26
  144. package/kits/docs/identity/PATH_OPS.md +16 -16
  145. package/kits/docs/identity/ROLE_DETAIL.md +25 -25
  146. package/kits/docs/identity/identity-tools.md +26 -26
  147. package/kits/docs/path-registry.md +62 -62
  148. package/kits/docs/prompt-loading-architecture.md +269 -269
  149. package/kits/docs/venues/aun-group.md +45 -45
  150. package/kits/docs/venues/aun-private.md +10 -10
  151. package/kits/docs/venues/client-desktop.md +10 -10
  152. package/kits/docs/venues/client-mobile.md +10 -10
  153. package/kits/docs/venues/feishu-group.md +13 -13
  154. package/kits/docs/venues/feishu-private.md +9 -9
  155. package/kits/docs/venues/group.md +37 -25
  156. package/kits/docs/venues/private.md +10 -10
  157. package/kits/eck_manifest.auxiliary.json +43 -43
  158. package/kits/eck_manifest.json +190 -190
  159. package/kits/eck_message_manifest.json +74 -74
  160. package/kits/migrations/README-role-config-v4.md +32 -32
  161. package/kits/migrations/migrate-contact-book-v2.mjs +748 -748
  162. package/kits/migrations/migrate-role-config-v4.mjs +614 -614
  163. package/kits/migrations/migrate-role-config-v5.mjs +365 -365
  164. package/kits/migrations/rename-config-file.mjs +99 -99
  165. package/kits/rules/01-overview.md +162 -162
  166. package/kits/rules/02-navigation.md +96 -96
  167. package/kits/rules/03-identity.md +36 -36
  168. package/kits/rules/04-relation.md +75 -75
  169. package/kits/rules/05-venue.md +60 -60
  170. package/kits/rules/06-channel.md +63 -63
  171. package/kits/schemas/_meta.json +53 -53
  172. package/kits/schemas/agent-config.schema.1.json +177 -177
  173. package/kits/schemas/agent-config.schema.10.json +395 -395
  174. package/kits/schemas/agent-config.schema.11.json +421 -421
  175. package/kits/schemas/agent-config.schema.12.json +427 -427
  176. package/kits/schemas/agent-config.schema.2.json +239 -239
  177. package/kits/schemas/agent-config.schema.3.json +119 -119
  178. package/kits/schemas/agent-config.schema.4.json +208 -208
  179. package/kits/schemas/agent-config.schema.5.json +326 -326
  180. package/kits/schemas/agent-config.schema.6.json +322 -322
  181. package/kits/schemas/agent-config.schema.7.json +303 -303
  182. package/kits/schemas/agent-config.schema.8.json +304 -304
  183. package/kits/schemas/agent-config.schema.9.json +364 -364
  184. package/kits/schemas/contact-book.schema.1.json +36 -36
  185. package/kits/schemas/contact-book.schema.2.json +43 -43
  186. package/kits/schemas/contact-book.schema.3.json +67 -67
  187. package/kits/schemas/daemon.schema.1.json +91 -91
  188. package/kits/schemas/daemon.schema.2.json +101 -101
  189. package/kits/schemas/daemon.schema.3.json +123 -123
  190. package/kits/schemas/daemon.schema.4.json +136 -136
  191. package/kits/schemas/daemon.schema.5.json +135 -135
  192. package/kits/schemas/daemon.schema.6.json +131 -131
  193. package/kits/schemas/defaults.schema.1.json +81 -81
  194. package/kits/schemas/defaults.schema.2.json +85 -85
  195. package/kits/schemas/defaults.schema.3.json +73 -73
  196. package/kits/schemas/defaults.schema.4.json +107 -107
  197. package/kits/schemas/defaults.schema.5.json +119 -119
  198. package/kits/schemas/menu-exec-schema-commands.md +208 -208
  199. package/kits/schemas/migrations/README.md +30 -30
  200. package/kits/schemas/relation-config.schema.1.json +158 -158
  201. package/kits/schemas/relation-config.schema.2.json +73 -73
  202. package/kits/schemas/relation-config.schema.3.json +50 -50
  203. package/kits/schemas/relation-config.schema.4.json +47 -47
  204. package/kits/schemas/relation-config.schema.5.json +47 -47
  205. package/kits/schemas/relation-config.schema.6.json +46 -46
  206. package/kits/schemas/relation-config.schema.7.json +59 -59
  207. package/kits/schemas/relation-config.schema.8.json +103 -103
  208. package/kits/schemas/role-config.schema.1.json +201 -201
  209. package/kits/schemas/role-registry.schema.1.json +35 -35
  210. package/kits/schemas/single-session.schema.1.json +31 -31
  211. package/kits/schemas/single-session.schema.2.json +57 -57
  212. package/kits/schemas/single-session.schema.3.json +32 -32
  213. package/kits/templates/bootstrap-welcome.md +17 -17
  214. package/kits/templates/message-fragments/handoff-context-to-target.md +9 -9
  215. package/kits/templates/message-fragments/handoff-request-to-target.md +19 -19
  216. package/kits/templates/message-fragments/handoff-response-to-origin.md +9 -9
  217. package/kits/templates/message-fragments/inject-default.md +2 -2
  218. package/kits/templates/message-fragments/item.md +2 -2
  219. package/kits/templates/roles/admin.json +92 -92
  220. package/kits/templates/roles/member.json +96 -96
  221. package/kits/templates/roles/owner.json +9 -9
  222. package/kits/templates/roles/visitor.json +85 -85
  223. package/kits/templates/system-fragments/baseagent.md +14 -14
  224. package/kits/templates/system-fragments/bootstrap.md +31 -29
  225. package/kits/templates/system-fragments/channel.md +64 -64
  226. package/kits/templates/system-fragments/commands.md +28 -28
  227. package/kits/templates/system-fragments/identity.md +13 -13
  228. package/kits/templates/system-fragments/relation.md +19 -19
  229. package/kits/templates/system-fragments/session.md +59 -59
  230. package/kits/templates/system-fragments/venue.md +31 -31
  231. package/package.json +100 -98
@@ -1,212 +1,212 @@
1
- import fs from 'node:fs';
2
- import net from 'node:net';
3
- import os from 'node:os';
4
- import path from 'node:path';
5
- import { randomUUID } from 'node:crypto';
6
-
7
- const startedAt = Date.now();
8
- const runId = randomUUID();
9
- let logPath;
10
- let logFailed = false;
11
- let context = {};
12
-
13
- // This file is also copied into the Linux runtime; keep diagnostics self-contained.
14
- function diagnostic(event, details = {}) {
15
- if (process.env.EVOLCORE_CODEX_HOOK_LOG === '0' || logFailed) return;
16
- try {
17
- if (!logPath) {
18
- logPath = path.resolve(process.env.EVOLCORE_CODEX_HOOK_LOG
19
- || path.join(process.env.CODEX_HOME || path.join(os.homedir(), '.codex'), 'log', 'evolcore-hook.jsonl'));
20
- fs.mkdirSync(path.dirname(logPath), { recursive: true, mode: 0o700 });
21
- // Rotation is best-effort when concurrent tool calls start together.
22
- try {
23
- if (fs.statSync(logPath).size >= 2 * 1024 * 1024) fs.renameSync(logPath, `${logPath}.1`);
24
- } catch {}
25
- }
26
- fs.appendFileSync(logPath, JSON.stringify({
27
- time: new Date().toISOString(), runId, pid: process.pid,
28
- elapsedMs: Date.now() - startedAt, event, ...context, ...details,
29
- }) + '\n', { mode: 0o600 });
30
- } catch (error) {
31
- logFailed = true;
32
- process.stderr.write(`[evolcore] Hook diagnostic log unavailable (${error?.code || 'unknown'}): ${logPath || '(unresolved)'}\n`);
33
- }
34
- }
35
-
36
- function errorDetails(error) {
37
- // JSON parser messages and policy reasons can contain tool input. Omit them.
38
- return {
39
- errorName: error?.name,
40
- errorCode: error?.code,
41
- syscall: error?.syscall,
42
- frames: typeof error?.stack === 'string'
43
- ? error.stack.split('\n').filter(line => /^\s+at /.test(line)).slice(0, 6) : undefined,
44
- };
45
- }
46
-
47
- process.on('uncaughtExceptionMonitor', error => diagnostic('uncaught_exception', errorDetails(error)));
48
- process.on('exit', code => diagnostic('exit', { exitCode: code }));
49
- diagnostic('start', {
50
- nodeVersion: process.version, execPath: process.execPath, entrypoint: process.argv[1],
51
- argumentCount: Math.max(0, process.argv.length - 2),
52
- socketArgumentPresent: Boolean(process.argv[2]),
53
- socketEnvironmentPresent: Boolean(process.env.EVOLCORE_INSTANCE_SOCKET),
54
- socketArgumentUnexpanded: /^%EVOLCORE_INSTANCE_SOCKET%$/i.test(process.argv[2] || ''),
55
- socketSource: process.argv[2] ? 'argument' : process.env.EVOLCORE_INSTANCE_SOCKET ? 'environment' : 'missing',
56
- });
57
-
58
- // Only bound socket establishment. Once connected, the official Codex hook
59
- // timeout owns the lifetime of this process; an application timeout here would
60
- // turn /pause into a denial/allow race.
61
- const connectTimeoutMs = 1500;
62
-
63
- function deny(reason, category) {
64
- diagnostic('decision', { decision: 'deny', category });
65
- process.stdout.write(JSON.stringify({
66
- hookSpecificOutput: {
67
- hookEventName: 'PreToolUse',
68
- permissionDecision: 'deny',
69
- permissionDecisionReason: reason || 'EvolCore policy denied this tool call.',
70
- },
71
- }));
72
- // Exit 2 is the documented blocking transport. Keep the structured JSON as
73
- // well so Codex can surface the policy reason through the PreToolUse result.
74
- process.stderr.write(`${reason || 'EvolCore policy denied this tool call.'}\n`);
75
- process.exitCode = 2;
76
- }
77
-
78
- function queryDaemon(socketPath, payload) {
79
- diagnostic('connect_start', { timeoutMs: connectTimeoutMs });
80
- return new Promise(resolve => {
81
- const socket = net.createConnection(socketPath);
82
- let buffer = '';
83
- let settled = false;
84
- let timer;
85
- let connected = false;
86
- const finish = value => {
87
- if (settled) return;
88
- settled = true;
89
- clearTimeout(timer);
90
- socket.destroy();
91
- resolve(value);
92
- };
93
- timer = setTimeout(() => {
94
- if (!connected) {
95
- diagnostic('connect_timeout');
96
- finish(null);
97
- }
98
- }, connectTimeoutMs);
99
- timer.unref?.();
100
- socket.once('error', error => {
101
- diagnostic('socket_error', { connected, ...errorDetails(error) });
102
- finish(null);
103
- });
104
- socket.once('close', () => {
105
- if (!settled) diagnostic('socket_closed_without_response', { connected, responseBytes: Buffer.byteLength(buffer) });
106
- });
107
- socket.once('connect', () => {
108
- connected = true;
109
- clearTimeout(timer);
110
- diagnostic('connected');
111
- socket.write(JSON.stringify(payload) + '\n');
112
- });
113
- socket.on('data', chunk => {
114
- buffer += String(chunk);
115
- const index = buffer.indexOf('\n');
116
- if (index < 0) return;
117
- try {
118
- const result = JSON.parse(buffer.slice(0, index));
119
- diagnostic('response', {
120
- ok: result?.ok === true, applicable: result?.applicable === true,
121
- decision: ['allow', 'deny'].includes(result?.decision) ? result.decision : 'other',
122
- reasonPresent: typeof result?.reason === 'string',
123
- });
124
- finish(result);
125
- } catch (error) {
126
- diagnostic('invalid_response', { responseBytes: Buffer.byteLength(buffer), ...errorDetails(error) });
127
- finish(null);
128
- }
129
- });
130
- });
131
- }
132
-
133
- // The Windows managed requirements are machine-wide, so unmanaged Codex
134
- // processes also reach this hook. EvolCore-owned threads carry a per-thread
135
- // credential (EVOLCORE_CODEX_SESSION_TOKEN, injected through the thread's
136
- // shell environment policy); unmanaged ones do not. Unmanaged calls are
137
- // allowed outright and never contact the daemon — EvolCore being stopped must
138
- // not break a standalone Codex. Credentialed calls always require an explicit
139
- // daemon allow: a credential without its daemon is an orphaned managed thread
140
- // and fails closed.
141
- async function main() {
142
- // Windows managed requirements use one machine-wide command. Resolve the
143
- // per-daemon named pipe from the inherited environment so multiple EvolCore
144
- // homes can share the installed hook without rewriting requirements.toml.
145
- const socketPath = process.argv[2] || process.env.EVOLCORE_INSTANCE_SOCKET;
146
- let input;
147
- try {
148
- input = JSON.parse(fs.readFileSync(0, 'utf8'));
149
- } catch (error) {
150
- diagnostic('invalid_input', errorDetails(error));
151
- deny('Invalid Codex PreToolUse payload.', 'invalid_input');
152
- return;
153
- }
1
+ import fs from 'node:fs';
2
+ import net from 'node:net';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { randomUUID } from 'node:crypto';
6
+
7
+ const startedAt = Date.now();
8
+ const runId = randomUUID();
9
+ let logPath;
10
+ let logFailed = false;
11
+ let context = {};
12
+
13
+ // This file is also copied into the Linux runtime; keep diagnostics self-contained.
14
+ function diagnostic(event, details = {}) {
15
+ if (process.env.EVOLCORE_CODEX_HOOK_LOG === '0' || logFailed) return;
16
+ try {
17
+ if (!logPath) {
18
+ logPath = path.resolve(process.env.EVOLCORE_CODEX_HOOK_LOG
19
+ || path.join(process.env.CODEX_HOME || path.join(os.homedir(), '.codex'), 'log', 'evolcore-hook.jsonl'));
20
+ fs.mkdirSync(path.dirname(logPath), { recursive: true, mode: 0o700 });
21
+ // Rotation is best-effort when concurrent tool calls start together.
22
+ try {
23
+ if (fs.statSync(logPath).size >= 2 * 1024 * 1024) fs.renameSync(logPath, `${logPath}.1`);
24
+ } catch {}
25
+ }
26
+ fs.appendFileSync(logPath, JSON.stringify({
27
+ time: new Date().toISOString(), runId, pid: process.pid,
28
+ elapsedMs: Date.now() - startedAt, event, ...context, ...details,
29
+ }) + '\n', { mode: 0o600 });
30
+ } catch (error) {
31
+ logFailed = true;
32
+ process.stderr.write(`[evolcore] Hook diagnostic log unavailable (${error?.code || 'unknown'}): ${logPath || '(unresolved)'}\n`);
33
+ }
34
+ }
35
+
36
+ function errorDetails(error) {
37
+ // JSON parser messages and policy reasons can contain tool input. Omit them.
38
+ return {
39
+ errorName: error?.name,
40
+ errorCode: error?.code,
41
+ syscall: error?.syscall,
42
+ frames: typeof error?.stack === 'string'
43
+ ? error.stack.split('\n').filter(line => /^\s+at /.test(line)).slice(0, 6) : undefined,
44
+ };
45
+ }
46
+
47
+ process.on('uncaughtExceptionMonitor', error => diagnostic('uncaught_exception', errorDetails(error)));
48
+ process.on('exit', code => diagnostic('exit', { exitCode: code }));
49
+ diagnostic('start', {
50
+ nodeVersion: process.version, execPath: process.execPath, entrypoint: process.argv[1],
51
+ argumentCount: Math.max(0, process.argv.length - 2),
52
+ socketArgumentPresent: Boolean(process.argv[2]),
53
+ socketEnvironmentPresent: Boolean(process.env.EVOLCORE_INSTANCE_SOCKET),
54
+ socketArgumentUnexpanded: /^%EVOLCORE_INSTANCE_SOCKET%$/i.test(process.argv[2] || ''),
55
+ socketSource: process.argv[2] ? 'argument' : process.env.EVOLCORE_INSTANCE_SOCKET ? 'environment' : 'missing',
56
+ });
57
+
58
+ // Only bound socket establishment. Once connected, the official Codex hook
59
+ // timeout owns the lifetime of this process; an application timeout here would
60
+ // turn /pause into a denial/allow race.
61
+ const connectTimeoutMs = 1500;
62
+
63
+ function deny(reason, category) {
64
+ diagnostic('decision', { decision: 'deny', category });
65
+ process.stdout.write(JSON.stringify({
66
+ hookSpecificOutput: {
67
+ hookEventName: 'PreToolUse',
68
+ permissionDecision: 'deny',
69
+ permissionDecisionReason: reason || 'EvolCore policy denied this tool call.',
70
+ },
71
+ }));
72
+ // Exit 2 is the documented blocking transport. Keep the structured JSON as
73
+ // well so Codex can surface the policy reason through the PreToolUse result.
74
+ process.stderr.write(`${reason || 'EvolCore policy denied this tool call.'}\n`);
75
+ process.exitCode = 2;
76
+ }
77
+
78
+ function queryDaemon(socketPath, payload) {
79
+ diagnostic('connect_start', { timeoutMs: connectTimeoutMs });
80
+ return new Promise(resolve => {
81
+ const socket = net.createConnection(socketPath);
82
+ let buffer = '';
83
+ let settled = false;
84
+ let timer;
85
+ let connected = false;
86
+ const finish = value => {
87
+ if (settled) return;
88
+ settled = true;
89
+ clearTimeout(timer);
90
+ socket.destroy();
91
+ resolve(value);
92
+ };
93
+ timer = setTimeout(() => {
94
+ if (!connected) {
95
+ diagnostic('connect_timeout');
96
+ finish(null);
97
+ }
98
+ }, connectTimeoutMs);
99
+ timer.unref?.();
100
+ socket.once('error', error => {
101
+ diagnostic('socket_error', { connected, ...errorDetails(error) });
102
+ finish(null);
103
+ });
104
+ socket.once('close', () => {
105
+ if (!settled) diagnostic('socket_closed_without_response', { connected, responseBytes: Buffer.byteLength(buffer) });
106
+ });
107
+ socket.once('connect', () => {
108
+ connected = true;
109
+ clearTimeout(timer);
110
+ diagnostic('connected');
111
+ socket.write(JSON.stringify(payload) + '\n');
112
+ });
113
+ socket.on('data', chunk => {
114
+ buffer += String(chunk);
115
+ const index = buffer.indexOf('\n');
116
+ if (index < 0) return;
117
+ try {
118
+ const result = JSON.parse(buffer.slice(0, index));
119
+ diagnostic('response', {
120
+ ok: result?.ok === true, applicable: result?.applicable === true,
121
+ decision: ['allow', 'deny'].includes(result?.decision) ? result.decision : 'other',
122
+ reasonPresent: typeof result?.reason === 'string',
123
+ });
124
+ finish(result);
125
+ } catch (error) {
126
+ diagnostic('invalid_response', { responseBytes: Buffer.byteLength(buffer), ...errorDetails(error) });
127
+ finish(null);
128
+ }
129
+ });
130
+ });
131
+ }
132
+
133
+ // The Windows managed requirements are machine-wide, so unmanaged Codex
134
+ // processes also reach this hook. EvolCore-owned threads carry a per-thread
135
+ // credential (EVOLCORE_CODEX_SESSION_TOKEN, injected through the thread's
136
+ // shell environment policy); unmanaged ones do not. Unmanaged calls are
137
+ // allowed outright and never contact the daemon — EvolCore being stopped must
138
+ // not break a standalone Codex. Credentialed calls always require an explicit
139
+ // daemon allow: a credential without its daemon is an orphaned managed thread
140
+ // and fails closed.
141
+ async function main() {
142
+ // Windows managed requirements use one machine-wide command. Resolve the
143
+ // per-daemon named pipe from the inherited environment so multiple EvolCore
144
+ // homes can share the installed hook without rewriting requirements.toml.
145
+ const socketPath = process.argv[2] || process.env.EVOLCORE_INSTANCE_SOCKET;
146
+ let input;
147
+ try {
148
+ input = JSON.parse(fs.readFileSync(0, 'utf8'));
149
+ } catch (error) {
150
+ diagnostic('invalid_input', errorDetails(error));
151
+ deny('Invalid Codex PreToolUse payload.', 'invalid_input');
152
+ return;
153
+ }
154
154
  context = {
155
- sessionId: typeof input?.session_id === 'string' ? input.session_id.slice(0, 128) : undefined,
156
- toolName: typeof input?.tool_name === 'string' ? input.tool_name.slice(0, 128) : undefined,
157
- callId: typeof (input?.tool_use_id ?? input?.toolUseId ?? input?.item_id ?? input?.itemId) === 'string'
158
- ? (input.tool_use_id ?? input.toolUseId ?? input.item_id ?? input.itemId).slice(0, 128) : undefined,
159
- };
160
- diagnostic('input', {
161
- preToolUse: input?.hook_event_name === 'PreToolUse',
162
- sessionIdPresent: typeof input?.session_id === 'string' && Boolean(input.session_id),
163
- toolNamePresent: typeof input?.tool_name === 'string' && Boolean(input.tool_name),
164
- toolInputPresent: input?.tool_input !== undefined,
165
- });
155
+ sessionId: typeof input?.session_id === 'string' ? input.session_id.slice(0, 128) : undefined,
156
+ toolName: typeof input?.tool_name === 'string' ? input.tool_name.slice(0, 128) : undefined,
157
+ callId: typeof (input?.tool_use_id ?? input?.toolUseId ?? input?.item_id ?? input?.itemId) === 'string'
158
+ ? (input.tool_use_id ?? input.toolUseId ?? input.item_id ?? input.itemId).slice(0, 128) : undefined,
159
+ };
160
+ diagnostic('input', {
161
+ preToolUse: input?.hook_event_name === 'PreToolUse',
162
+ sessionIdPresent: typeof input?.session_id === 'string' && Boolean(input.session_id),
163
+ toolNamePresent: typeof input?.tool_name === 'string' && Boolean(input.tool_name),
164
+ toolInputPresent: input?.tool_input !== undefined,
165
+ });
166
166
  if (!socketPath
167
167
  || input?.hook_event_name !== 'PreToolUse'
168
168
  || typeof input?.session_id !== 'string'
169
- || !input.session_id
170
- || typeof input?.tool_name !== 'string'
171
- || !input.tool_name) {
172
- deny('Incomplete Codex PreToolUse payload.', 'incomplete_input');
173
- return;
174
- }
175
-
176
- const sessionToken = typeof process.env.EVOLCORE_CODEX_SESSION_TOKEN === 'string'
177
- && process.env.EVOLCORE_CODEX_SESSION_TOKEN
178
- ? process.env.EVOLCORE_CODEX_SESSION_TOKEN
179
- : undefined;
180
-
181
- if (!sessionToken) {
182
- // Unmanaged process: nothing to enforce, and no reason to pay the daemon
183
- // round-trip (or its outage) for a call we would allow anyway.
184
- return;
185
- }
186
- if (!socketPath) {
187
- deny('EvolCore session credential is present but the policy service address is missing.');
188
- return;
189
- }
190
-
191
- const result = await queryDaemon(socketPath, {
192
- type: 'codex-pre-tool-use',
193
- threadId: input.session_id,
194
- toolName: input.tool_name,
195
- toolInput: input.tool_input ?? null,
196
- sessionToken,
197
- ...(typeof (input.tool_use_id ?? input.toolUseId ?? input.item_id ?? input.itemId) === 'string'
198
- ? { callId: input.tool_use_id ?? input.toolUseId ?? input.item_id ?? input.itemId }
199
- : {}),
200
- });
201
- if (!result) {
202
- deny('EvolCore policy service is unavailable.', 'service_unavailable');
203
- return;
204
- }
205
- if (result.ok === true && result.applicable === true && result.decision === 'allow') {
206
- diagnostic('decision', { decision: 'allow' });
207
- return;
208
- }
209
- deny(result.reason || 'EvolCore policy did not explicitly allow this tool call.', 'policy_denied');
210
- }
211
-
212
- void main();
169
+ || !input.session_id
170
+ || typeof input?.tool_name !== 'string'
171
+ || !input.tool_name) {
172
+ deny('Incomplete Codex PreToolUse payload.', 'incomplete_input');
173
+ return;
174
+ }
175
+
176
+ const sessionToken = typeof process.env.EVOLCORE_CODEX_SESSION_TOKEN === 'string'
177
+ && process.env.EVOLCORE_CODEX_SESSION_TOKEN
178
+ ? process.env.EVOLCORE_CODEX_SESSION_TOKEN
179
+ : undefined;
180
+
181
+ if (!sessionToken) {
182
+ // Unmanaged process: nothing to enforce, and no reason to pay the daemon
183
+ // round-trip (or its outage) for a call we would allow anyway.
184
+ return;
185
+ }
186
+ if (!socketPath) {
187
+ deny('EvolCore session credential is present but the policy service address is missing.');
188
+ return;
189
+ }
190
+
191
+ const result = await queryDaemon(socketPath, {
192
+ type: 'codex-pre-tool-use',
193
+ threadId: input.session_id,
194
+ toolName: input.tool_name,
195
+ toolInput: input.tool_input ?? null,
196
+ sessionToken,
197
+ ...(typeof (input.tool_use_id ?? input.toolUseId ?? input.item_id ?? input.itemId) === 'string'
198
+ ? { callId: input.tool_use_id ?? input.toolUseId ?? input.item_id ?? input.itemId }
199
+ : {}),
200
+ });
201
+ if (!result) {
202
+ deny('EvolCore policy service is unavailable.', 'service_unavailable');
203
+ return;
204
+ }
205
+ if (result.ok === true && result.applicable === true && result.decision === 'allow') {
206
+ diagnostic('decision', { decision: 'allow' });
207
+ return;
208
+ }
209
+ deny(result.reason || 'EvolCore policy did not explicitly allow this tool call.', 'policy_denied');
210
+ }
211
+
212
+ void main();