evolcore 0.0.24 → 0.0.25

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 (197) hide show
  1. package/CHANGELOG.md +578 -561
  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 +29 -29
  8. package/bin/install-codex-managed-hooks.mjs +555 -555
  9. package/dist/agents/claude-runner.js +4 -1
  10. package/dist/agents/codex-runner.js +7 -4
  11. package/dist/agents/ecagent-runner.js +14 -13
  12. package/dist/agents/gemini-runner.js +22 -2
  13. package/dist/aun/aid/agentmd.js +56 -4
  14. package/dist/aun/aid/index.js +1 -0
  15. package/dist/aun/aid/managed-operation.js +53 -1
  16. package/dist/aun/storage/client.js +4 -1
  17. package/dist/aun/storage/index.js +1 -1
  18. package/dist/cli/agent-command.js +186 -87
  19. package/dist/cli/agent-create.js +42 -4
  20. package/dist/cli/agent.js +103 -1
  21. package/dist/cli/aun-commands.js +360 -294
  22. package/dist/cli/bench.js +20 -20
  23. package/dist/cli/config.js +20 -20
  24. package/dist/cli/contact.js +10 -10
  25. package/dist/cli/ctl-command.js +22 -22
  26. package/dist/cli/daemon-commands.js +15 -15
  27. package/dist/cli/fs-command.js +67 -67
  28. package/dist/cli/handoff-command.js +7 -7
  29. package/dist/cli/index.js +77 -77
  30. package/dist/cli/link-rules.js +21 -21
  31. package/dist/cli/menu-client.js +11 -11
  32. package/dist/cli/net-check.js +22 -22
  33. package/dist/cli/queue-command.js +29 -29
  34. package/dist/cli/response.js +30 -30
  35. package/dist/cli/restart-monitor.js +30 -30
  36. package/dist/cli/stats.js +51 -51
  37. package/dist/cli/trigger-command.js +56 -56
  38. package/dist/config/builtin-roles.js +1 -0
  39. package/dist/config/config-manager.js +15 -0
  40. package/dist/config/config-operation-service.js +20 -3
  41. package/dist/config/resolved-config-op.js +39 -1
  42. package/dist/core/auth/operation-authorizer.js +62 -4
  43. package/dist/core/auth/operation-catalog.js +16 -0
  44. package/dist/core/command/agent-control.js +54 -25
  45. package/dist/core/command/cli-intent-parser.js +18 -0
  46. package/dist/core/command/command-handler.js +91 -16
  47. package/dist/core/command/menu-catalog.js +1 -0
  48. package/dist/core/command/menu-handler.js +41 -5
  49. package/dist/core/command/menu-protocol.js +1 -0
  50. package/dist/core/command/slash-handler.js +9 -9
  51. package/dist/core/event-catalog.js +5 -0
  52. package/dist/core/evolagent.js +3 -3
  53. package/dist/core/message/response-engine.js +12 -6
  54. package/dist/core/permission/tool-policy.js +45 -6
  55. package/dist/index.js +209 -162
  56. package/dist/stats/db.js +159 -159
  57. package/dist/stats/eck-vars.js +9 -9
  58. package/dist/stats/query.js +139 -139
  59. package/dist/stats/role-budget.js +6 -6
  60. package/dist/stats/writer.js +51 -51
  61. package/dist/utils/avatar-upload.js +282 -23
  62. package/dist/utils/error-dict.json +160 -160
  63. package/dist/utils/macos-autostart.js +22 -22
  64. package/dist/utils/model-prices.jsonl +23 -23
  65. package/dist/utils/welcome.js +22 -22
  66. package/ecagent/LICENSE +21 -21
  67. package/ecagent/NOTICE +11 -11
  68. package/ecagent/README.md +61 -61
  69. package/ecagent/dist/harness/compaction/branch-summarization.js +30 -30
  70. package/ecagent/dist/harness/compaction/compaction.js +83 -83
  71. package/ecagent/dist/harness/messages.js +7 -7
  72. package/kits/docs/GUIDE.md +20 -20
  73. package/kits/docs/INDEX.md +66 -66
  74. package/kits/docs/aun/CHEATSHEET.md +19 -19
  75. package/kits/docs/aun/SYNC_PROTOCOL.md +15 -15
  76. package/kits/docs/channels/aun.md +56 -56
  77. package/kits/docs/channels/feishu.md +56 -56
  78. package/kits/docs/context-assembly.md +365 -364
  79. package/kits/docs/eck_templates/GUIDE.template.md +22 -22
  80. package/kits/docs/eck_templates/INDEX.template.md +28 -28
  81. package/kits/docs/eck_templates/path-registry.template.md +33 -33
  82. package/kits/docs/eck_templates/runtime.template.md +19 -19
  83. package/kits/docs/evolcore/INDEX.md +71 -69
  84. package/kits/docs/evolcore/agent-create.md +170 -156
  85. package/kits/docs/evolcore/agent.md +83 -73
  86. package/kits/docs/evolcore/aid.md +64 -62
  87. package/kits/docs/evolcore/codex-windows-sandbox.md +146 -0
  88. package/kits/docs/evolcore/config.md +195 -195
  89. package/kits/docs/evolcore/contact.md +68 -68
  90. package/kits/docs/evolcore/ctl.md +46 -46
  91. package/kits/docs/evolcore/event.md +216 -216
  92. package/kits/docs/evolcore/fs-architecture.md +1215 -1215
  93. package/kits/docs/evolcore/fs.md +110 -110
  94. package/kits/docs/evolcore/group-collaboration.md +243 -243
  95. package/kits/docs/evolcore/group-fs.md +17 -17
  96. package/kits/docs/evolcore/group-rules.md +251 -251
  97. package/kits/docs/evolcore/group.md +152 -152
  98. package/kits/docs/evolcore/menu-client.md +34 -34
  99. package/kits/docs/evolcore/model.md +50 -50
  100. package/kits/docs/evolcore/msg.md +165 -165
  101. package/kits/docs/evolcore/response.md +75 -75
  102. package/kits/docs/evolcore/rpc.md +37 -37
  103. package/kits/docs/evolcore/self-summary.md +29 -29
  104. package/kits/docs/evolcore/stats.md +83 -83
  105. package/kits/docs/evolcore/storage.md +50 -50
  106. package/kits/docs/evolcore/trigger.md +575 -575
  107. package/kits/docs/identity/AID_PROFILE_SPEC.md +26 -26
  108. package/kits/docs/identity/PATH_OPS.md +16 -16
  109. package/kits/docs/identity/ROLE_DETAIL.md +25 -25
  110. package/kits/docs/identity/identity-tools.md +26 -26
  111. package/kits/docs/path-registry.md +62 -62
  112. package/kits/docs/prompt-loading-architecture.md +270 -269
  113. package/kits/docs/venues/aun-group.md +45 -45
  114. package/kits/docs/venues/aun-private.md +10 -10
  115. package/kits/docs/venues/client-desktop.md +10 -10
  116. package/kits/docs/venues/client-mobile.md +10 -10
  117. package/kits/docs/venues/feishu-group.md +13 -13
  118. package/kits/docs/venues/feishu-private.md +9 -9
  119. package/kits/docs/venues/group.md +25 -25
  120. package/kits/docs/venues/private.md +10 -10
  121. package/kits/eck_manifest.auxiliary.json +43 -43
  122. package/kits/eck_manifest.json +195 -183
  123. package/kits/eck_message_manifest.json +74 -74
  124. package/kits/migrations/README-role-config-v4.md +32 -32
  125. package/kits/migrations/migrate-contact-book-v2.mjs +748 -748
  126. package/kits/migrations/migrate-role-config-v4.mjs +614 -614
  127. package/kits/migrations/migrate-role-config-v5.mjs +365 -365
  128. package/kits/migrations/rename-config-file.mjs +99 -99
  129. package/kits/rules/01-overview.md +162 -162
  130. package/kits/rules/02-navigation.md +96 -96
  131. package/kits/rules/03-identity.md +39 -37
  132. package/kits/rules/04-relation.md +75 -75
  133. package/kits/rules/05-venue.md +60 -60
  134. package/kits/rules/06-channel.md +63 -63
  135. package/kits/schemas/_meta.json +53 -53
  136. package/kits/schemas/agent-config.schema.1.json +177 -177
  137. package/kits/schemas/agent-config.schema.10.json +395 -395
  138. package/kits/schemas/agent-config.schema.11.json +421 -421
  139. package/kits/schemas/agent-config.schema.12.json +427 -427
  140. package/kits/schemas/agent-config.schema.2.json +239 -239
  141. package/kits/schemas/agent-config.schema.3.json +119 -119
  142. package/kits/schemas/agent-config.schema.4.json +208 -208
  143. package/kits/schemas/agent-config.schema.5.json +326 -326
  144. package/kits/schemas/agent-config.schema.6.json +322 -322
  145. package/kits/schemas/agent-config.schema.7.json +303 -303
  146. package/kits/schemas/agent-config.schema.8.json +304 -304
  147. package/kits/schemas/agent-config.schema.9.json +364 -364
  148. package/kits/schemas/contact-book.schema.1.json +36 -36
  149. package/kits/schemas/contact-book.schema.2.json +43 -43
  150. package/kits/schemas/contact-book.schema.3.json +67 -67
  151. package/kits/schemas/daemon.schema.1.json +91 -91
  152. package/kits/schemas/daemon.schema.2.json +101 -101
  153. package/kits/schemas/daemon.schema.3.json +123 -123
  154. package/kits/schemas/daemon.schema.4.json +136 -136
  155. package/kits/schemas/daemon.schema.5.json +135 -135
  156. package/kits/schemas/daemon.schema.6.json +131 -131
  157. package/kits/schemas/defaults.schema.1.json +81 -81
  158. package/kits/schemas/defaults.schema.2.json +85 -85
  159. package/kits/schemas/defaults.schema.3.json +73 -73
  160. package/kits/schemas/defaults.schema.4.json +107 -107
  161. package/kits/schemas/defaults.schema.5.json +119 -119
  162. package/kits/schemas/menu-exec-schema-commands.md +208 -208
  163. package/kits/schemas/migrations/README.md +30 -30
  164. package/kits/schemas/relation-config.schema.1.json +158 -158
  165. package/kits/schemas/relation-config.schema.2.json +73 -73
  166. package/kits/schemas/relation-config.schema.3.json +50 -50
  167. package/kits/schemas/relation-config.schema.4.json +47 -47
  168. package/kits/schemas/relation-config.schema.5.json +47 -47
  169. package/kits/schemas/relation-config.schema.6.json +46 -46
  170. package/kits/schemas/relation-config.schema.7.json +59 -59
  171. package/kits/schemas/relation-config.schema.8.json +103 -103
  172. package/kits/schemas/role-config.schema.1.json +201 -201
  173. package/kits/schemas/role-registry.schema.1.json +35 -35
  174. package/kits/schemas/single-session.schema.1.json +31 -31
  175. package/kits/schemas/single-session.schema.2.json +57 -57
  176. package/kits/schemas/single-session.schema.3.json +32 -32
  177. package/kits/templates/bootstrap-welcome.md +17 -17
  178. package/kits/templates/message-fragments/handoff-context-to-target.md +9 -9
  179. package/kits/templates/message-fragments/handoff-request-to-target.md +19 -19
  180. package/kits/templates/message-fragments/handoff-response-to-origin.md +9 -9
  181. package/kits/templates/message-fragments/inject-default.md +2 -2
  182. package/kits/templates/message-fragments/item.md +2 -2
  183. package/kits/templates/roles/admin.json +96 -84
  184. package/kits/templates/roles/member.json +97 -96
  185. package/kits/templates/roles/owner.json +9 -9
  186. package/kits/templates/roles/visitor.json +86 -85
  187. package/kits/templates/system-fragments/baseagent.md +14 -14
  188. package/kits/templates/system-fragments/bootstrap.md +38 -29
  189. package/kits/templates/system-fragments/channel.md +64 -64
  190. package/kits/templates/system-fragments/commands.md +28 -28
  191. package/kits/templates/system-fragments/identity.md +13 -13
  192. package/kits/templates/system-fragments/relation.md +19 -19
  193. package/kits/templates/system-fragments/session.md +59 -59
  194. package/kits/templates/system-fragments/venue.md +31 -31
  195. package/package.json +98 -98
  196. package/skills/eclink/SKILL.md +0 -84
  197. package/skills/eclink/agents/openai.yaml +0 -4
@@ -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();