evolcore 0.0.10 → 0.0.12

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 (131) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +3 -3
  3. package/bin/codex-managed-hook.mjs +80 -0
  4. package/dist/agents/baseagent.js +4 -0
  5. package/dist/agents/claude-runner.js +158 -44
  6. package/dist/agents/codex-app-server-client.js +153 -23
  7. package/dist/agents/codex-runner.js +331 -43
  8. package/dist/agents/ecagent-runner.js +53 -6
  9. package/dist/agents/gemini-runner.js +1 -0
  10. package/dist/agents/request-identity.js +55 -0
  11. package/dist/aun/aid/client.js +11 -21
  12. package/dist/aun/aid/managed-operation.js +107 -0
  13. package/dist/aun/msg/group.js +81 -12
  14. package/dist/aun/msg/managed-operation.js +804 -0
  15. package/dist/aun/msg/mention-schema.js +20 -0
  16. package/dist/aun/msg/p2p.js +7 -0
  17. package/dist/aun/outbox.js +28 -31
  18. package/dist/channels/aun.js +418 -235
  19. package/dist/channels/daemon.js +13 -0
  20. package/dist/cli/agent-command.js +72 -17
  21. package/dist/cli/agent.js +249 -22
  22. package/dist/cli/aun-commands.js +270 -69
  23. package/dist/cli/bench.js +12 -3
  24. package/dist/cli/daemon-commands.js +52 -33
  25. package/dist/cli/fs-command.js +60 -0
  26. package/dist/cli/handoff-command.js +3 -0
  27. package/dist/cli/index.js +106 -63
  28. package/dist/cli/init-cancel.js +208 -0
  29. package/dist/cli/init-channel.js +343 -195
  30. package/dist/cli/init.js +21 -9
  31. package/dist/cli/managed-command-guard.js +41 -0
  32. package/dist/cli/model.js +11 -4
  33. package/dist/cli/queue-command.js +11 -1
  34. package/dist/cli/response.js +71 -0
  35. package/dist/cli/restart-monitor.js +4 -18
  36. package/dist/cli/trigger-command.js +4 -0
  37. package/dist/config/builtin-roles.js +5 -0
  38. package/dist/config/contact-book-store.js +11 -3
  39. package/dist/config/contact-request-service.js +161 -15
  40. package/dist/config/gateway-config.js +26 -10
  41. package/dist/config/mention-mode.js +8 -24
  42. package/dist/core/agent-reload-coordinator.js +53 -0
  43. package/dist/core/auth/agent-delegation.js +0 -1
  44. package/dist/core/auth/operation-authorizer.js +62 -154
  45. package/dist/core/auth/operation-catalog.js +264 -22
  46. package/dist/core/bootstrap-messages.js +68 -0
  47. package/dist/core/bootstrap-service.js +144 -10
  48. package/dist/core/capability/providers/claude-capability-provider.js +9 -31
  49. package/dist/core/capability/providers/codex-capability-provider.js +4 -15
  50. package/dist/core/capability/skill-discovery.js +55 -0
  51. package/dist/core/channel-loader.js +24 -7
  52. package/dist/core/command/agent-control.js +14 -11
  53. package/dist/core/command/cli-intent-parser.js +9 -0
  54. package/dist/core/command/command-handler.js +467 -6
  55. package/dist/core/command/connect-menu.js +15 -0
  56. package/dist/core/command/group-menu.js +4 -4
  57. package/dist/core/command/menu-catalog.js +366 -0
  58. package/dist/core/command/menu-handler.js +213 -110
  59. package/dist/core/command/menu-protocol.js +71 -0
  60. package/dist/core/command/slash-gate.js +5 -2
  61. package/dist/core/command/slash-handler.js +45 -25
  62. package/dist/core/daemon-file-cache.js +2 -1
  63. package/dist/core/data-migration.js +9 -2
  64. package/dist/core/evolagent-registry.js +125 -35
  65. package/dist/core/evolagent.js +12 -5
  66. package/dist/core/handoff/dispatcher.js +5 -2
  67. package/dist/core/handoff/runtime.js +145 -21
  68. package/dist/core/handoff/store.js +24 -0
  69. package/dist/core/handoff/types.js +1 -0
  70. package/dist/core/inference/text-inference.js +38 -4
  71. package/dist/core/message/logical-queue-bridge.js +1 -0
  72. package/dist/core/message/mention-schema.js +126 -0
  73. package/dist/core/message/message-bridge.js +237 -79
  74. package/dist/core/message/message-log.js +23 -0
  75. package/dist/core/message/message-queue.js +20 -4
  76. package/dist/core/message/response-engine.js +54 -36
  77. package/dist/core/model/model-catalog.js +143 -24
  78. package/dist/core/model/model-diagnostics.js +28 -10
  79. package/dist/core/permission/ec-command-parser.js +156 -10
  80. package/dist/core/permission/index.js +1 -0
  81. package/dist/core/permission/readonly-shell-query.js +532 -0
  82. package/dist/core/permission/sandbox-runtime.js +22 -1
  83. package/dist/core/permission/shell-environment.js +46 -0
  84. package/dist/core/permission/tool-policy.js +292 -111
  85. package/dist/core/protected-paths.js +17 -9
  86. package/dist/core/runner-reload-transaction.js +57 -0
  87. package/dist/core/session/session-manager.js +21 -2
  88. package/dist/eck/manifest-engine.js +39 -13
  89. package/dist/index.js +644 -180
  90. package/dist/ipc.js +246 -19
  91. package/dist/response-system/coordinator.js +6 -4
  92. package/dist/response-system/engines/v1/proactive-flow.js +26 -8
  93. package/dist/response-system/modes/single-session/index.js +14 -1
  94. package/dist/response-system/selector.js +6 -5
  95. package/dist/trigger/anomaly-store.js +4 -0
  96. package/dist/trigger/script-executor.js +1 -0
  97. package/dist/utils/aid-bind.js +3 -8
  98. package/dist/utils/codex-app-server-registry.js +90 -0
  99. package/dist/utils/codex-cli.js +5 -1
  100. package/dist/utils/cross-platform.js +15 -0
  101. package/dist/utils/log-writer.js +6 -10
  102. package/dist/utils/logger.js +5 -5
  103. package/dist/utils/npm-ops.js +5 -12
  104. package/dist/utils/tool-summary.js +11 -0
  105. package/kits/docs/channels/aun.md +1 -1
  106. package/kits/docs/context-assembly.md +2 -2
  107. package/kits/docs/evolcore/agent.md +11 -6
  108. package/kits/docs/evolcore/aid.md +14 -0
  109. package/kits/docs/evolcore/msg.md +13 -0
  110. package/kits/docs/prompt-loading-architecture.md +1 -2
  111. package/kits/docs/venues/group.md +1 -1
  112. package/kits/eck_manifest.json +0 -12
  113. package/kits/rules/01-overview.md +9 -0
  114. package/kits/rules/04-relation.md +1 -1
  115. package/kits/rules/05-venue.md +2 -2
  116. package/kits/schemas/agent-config.schema.3.json +1 -1
  117. package/kits/schemas/agent-config.schema.4.json +1 -1
  118. package/kits/schemas/relation-config.schema.2.json +1 -1
  119. package/kits/schemas/role-config.schema.1.json +1 -1
  120. package/kits/schemas/single-session.schema.1.json +3 -3
  121. package/kits/schemas/single-session.schema.2.json +3 -3
  122. package/kits/templates/roles/admin.json +27 -0
  123. package/kits/templates/roles/member.json +44 -0
  124. package/kits/templates/roles/visitor.json +40 -2
  125. package/kits/templates/system-fragments/bootstrap.md +12 -6
  126. package/kits/templates/system-fragments/channel.md +6 -0
  127. package/kits/templates/system-fragments/session.md +2 -0
  128. package/package.json +3 -2
  129. package/skills/eclink/SKILL.md +15 -3
  130. package/skills/eclink/agents/openai.yaml +3 -3
  131. package/dist/core/command/evol-menu-version-gate.js +0 -39
@@ -0,0 +1,532 @@
1
+ import path from 'path';
2
+ import { parseLiteralShellArgv } from './ec-command-parser.js';
3
+ const MAX_QUERY_LISTS = 8;
4
+ const MAX_PIPELINE_STAGES = 3;
5
+ const SUPPORTED_EXECUTABLES = new Set([
6
+ 'echo', 'pwd', 'ls', 'cat', 'grep', 'rg', 'find', 'sed', 'head', 'sort',
7
+ ]);
8
+ const FIND_MUTATING_PRIMARY_RE = /^-(?:delete|exec|execdir|ok|okdir|fprint|fprint0|fprintf|fls)$/;
9
+ const RG_PROCESS_SPAWNING_OPTION_RE = /^(?:--pre|--hostname-bin|--pre-glob|--search-zip)(?:=|$)/;
10
+ // The fd number must be adjacent to `>` in real shell grammar. Accepting
11
+ // `2 > /dev/null` would hide a literal file operand named `2` while actually
12
+ // redirecting stdout rather than stderr.
13
+ const STDERR_DEV_NULL_RE = /^(.*?)[ \t]+2>[ \t]*\/dev\/null[ \t]*$/;
14
+ function normalizeExecutable(value) {
15
+ const normalized = value.replace(/\\/g, '/');
16
+ const executable = path.posix.basename(normalized);
17
+ if (!SUPPORTED_EXECUTABLES.has(executable))
18
+ return undefined;
19
+ if (normalized === executable || normalized === `/bin/${executable}` || normalized === `/usr/bin/${executable}`) {
20
+ return executable;
21
+ }
22
+ return undefined;
23
+ }
24
+ function splitQueryStructure(command) {
25
+ const pipelines = [];
26
+ let pipeline = [];
27
+ let segment = '';
28
+ let quote = null;
29
+ let escaped = false;
30
+ const finishStage = () => {
31
+ const trimmed = segment.trim();
32
+ if (!trimmed)
33
+ return false;
34
+ pipeline.push(trimmed);
35
+ segment = '';
36
+ return pipeline.length <= MAX_PIPELINE_STAGES;
37
+ };
38
+ const finishPipeline = () => {
39
+ if (!finishStage())
40
+ return false;
41
+ pipelines.push(pipeline);
42
+ pipeline = [];
43
+ return pipelines.length <= MAX_QUERY_LISTS;
44
+ };
45
+ for (let index = 0; index < command.length; index++) {
46
+ const char = command[index];
47
+ if (escaped) {
48
+ segment += char;
49
+ escaped = false;
50
+ continue;
51
+ }
52
+ if (char === '\\') {
53
+ segment += char;
54
+ escaped = true;
55
+ continue;
56
+ }
57
+ if (quote === 'single') {
58
+ segment += char;
59
+ if (char === "'")
60
+ quote = null;
61
+ continue;
62
+ }
63
+ if (quote === 'double') {
64
+ segment += char;
65
+ if (char === '"')
66
+ quote = null;
67
+ continue;
68
+ }
69
+ if (char === "'" || char === '"') {
70
+ quote = char === "'" ? 'single' : 'double';
71
+ segment += char;
72
+ continue;
73
+ }
74
+ if (char === '\0' || char === '\r' || char === '\n' || char === '&' || char === '<')
75
+ return null;
76
+ if (char === '|') {
77
+ if (command[index + 1] === '|' || !finishStage())
78
+ return null;
79
+ continue;
80
+ }
81
+ if (char === ';') {
82
+ if (!finishPipeline())
83
+ return null;
84
+ continue;
85
+ }
86
+ segment += char;
87
+ }
88
+ if (escaped || quote || !finishPipeline())
89
+ return null;
90
+ return pipelines;
91
+ }
92
+ function parseStageArgv(segment) {
93
+ const redirectMatch = segment.match(STDERR_DEV_NULL_RE);
94
+ const withoutRedirect = redirectMatch?.[1] ?? segment;
95
+ if (withoutRedirect.includes('>'))
96
+ return null;
97
+ return parseLiteralShellArgv(withoutRedirect.trim());
98
+ }
99
+ function exact(value) {
100
+ return { value, access: 'exact' };
101
+ }
102
+ function recursive(value) {
103
+ return { value, access: 'recursive' };
104
+ }
105
+ function parseOptionsAndPaths(args, valueOptions, recursiveOptions = new Set()) {
106
+ const paths = [];
107
+ let optionsEnded = false;
108
+ let recursiveAccess = false;
109
+ for (let index = 0; index < args.length; index++) {
110
+ const argument = args[index];
111
+ if (!optionsEnded && argument === '--') {
112
+ optionsEnded = true;
113
+ continue;
114
+ }
115
+ if (!optionsEnded && argument.startsWith('-') && argument !== '-') {
116
+ const option = argument.split('=', 1)[0];
117
+ if (recursiveOptions.has(option) || [...recursiveOptions].some(entry => entry.length === 2 && argument.startsWith('-') && !argument.startsWith('--') && argument.slice(1).includes(entry[1]))) {
118
+ recursiveAccess = true;
119
+ }
120
+ if (!argument.includes('=') && valueOptions.has(option)) {
121
+ if (++index >= args.length)
122
+ return null;
123
+ }
124
+ continue;
125
+ }
126
+ if (argument !== '-')
127
+ paths.push(argument);
128
+ }
129
+ return { paths, recursive: recursiveAccess };
130
+ }
131
+ function analyzeLs(args) {
132
+ if (args.some(argument => argument === '--dereference'
133
+ || (!argument.startsWith('--') && /^-[^-]*L/.test(argument))))
134
+ return null;
135
+ const parsed = parseOptionsAndPaths(args, new Set([
136
+ '-I', '-T', '-w', '--block-size', '--color', '--format', '--hide', '--ignore',
137
+ '--indicator-style', '--quoting-style', '--sort', '--tabsize', '--time', '--time-style',
138
+ ]), new Set(['-R', '--recursive']));
139
+ if (!parsed)
140
+ return null;
141
+ const values = parsed.paths.length > 0 ? parsed.paths : ['.'];
142
+ return values.map(value => parsed.recursive ? recursive(value) : exact(value));
143
+ }
144
+ function analyzeCat(args) {
145
+ const parsed = parseOptionsAndPaths(args, new Set());
146
+ return parsed?.paths.map(exact) ?? null;
147
+ }
148
+ function consumeOptionValue(args, index, inline) {
149
+ if (inline)
150
+ return index;
151
+ return index + 1 < args.length ? index + 1 : null;
152
+ }
153
+ function analyzeGrep(args) {
154
+ const paths = [];
155
+ let optionsEnded = false;
156
+ let hasExplicitPattern = false;
157
+ let implicitPatternSeen = false;
158
+ let inputOperandSeen = false;
159
+ let recursiveAccess = false;
160
+ const textValueOptions = new Set([
161
+ '-A', '-B', '-C', '-m', '--after-context', '--before-context', '--binary-files',
162
+ '--color', '--context', '--directories', '--exclude', '--exclude-dir', '--group-separator',
163
+ '--include', '--label', '--max-count',
164
+ ]);
165
+ const patternFileOptions = new Set(['-f', '--file']);
166
+ const auxiliaryFileOptions = new Set(['--exclude-from']);
167
+ for (let index = 0; index < args.length; index++) {
168
+ const argument = args[index];
169
+ if (!optionsEnded && argument === '--') {
170
+ optionsEnded = true;
171
+ continue;
172
+ }
173
+ if (!optionsEnded && argument.startsWith('-') && argument !== '-') {
174
+ const shortBody = argument.startsWith('--') ? '' : argument.slice(1);
175
+ const inlineValueOption = shortBody.length > 1 && ['d', 'e', 'f'].includes(shortBody[0]);
176
+ if (!inlineValueOption && /[def]/.test(shortBody.slice(1)))
177
+ return null;
178
+ if (argument === '-R' || argument === '--dereference-recursive'
179
+ || (!inlineValueOption && shortBody.includes('R')))
180
+ return null;
181
+ if (argument === '-r' || argument === '--recursive'
182
+ || (!inlineValueOption && shortBody.includes('r'))) {
183
+ recursiveAccess = true;
184
+ }
185
+ const option = argument.split('=', 1)[0];
186
+ if (option === '--directories' || argument === '-d' || (!argument.startsWith('--') && argument.startsWith('-d'))) {
187
+ const inline = argument.includes('=') || (!argument.startsWith('--') && argument.length > 2);
188
+ const consumed = consumeOptionValue(args, index, inline);
189
+ if (consumed === null)
190
+ return null;
191
+ const value = argument.includes('=')
192
+ ? argument.slice(argument.indexOf('=') + 1)
193
+ : inline
194
+ ? argument.slice(2)
195
+ : args[consumed];
196
+ if (value === 'recurse')
197
+ recursiveAccess = true;
198
+ index = consumed;
199
+ continue;
200
+ }
201
+ const inline = argument.includes('=') || (!argument.startsWith('--') && argument.length > 2
202
+ && (argument[1] === 'e' || argument[1] === 'f'));
203
+ if (option === '-e' || option === '--regexp' || (!argument.startsWith('--') && argument.startsWith('-e'))) {
204
+ const consumed = consumeOptionValue(args, index, inline);
205
+ if (consumed === null)
206
+ return null;
207
+ index = consumed;
208
+ hasExplicitPattern = true;
209
+ continue;
210
+ }
211
+ const patternFileOption = patternFileOptions.has(option)
212
+ || (!argument.startsWith('--') && argument.startsWith('-f'));
213
+ const pathOption = patternFileOption || auxiliaryFileOptions.has(option);
214
+ if (pathOption) {
215
+ const consumed = consumeOptionValue(args, index, inline);
216
+ if (consumed === null)
217
+ return null;
218
+ if (inline) {
219
+ const value = argument.includes('=') ? argument.slice(argument.indexOf('=') + 1) : argument.slice(2);
220
+ if (value)
221
+ paths.push(exact(value));
222
+ }
223
+ else {
224
+ paths.push(exact(args[consumed]));
225
+ }
226
+ index = consumed;
227
+ if (patternFileOption)
228
+ hasExplicitPattern = true;
229
+ continue;
230
+ }
231
+ if (textValueOptions.has(option)) {
232
+ const consumed = consumeOptionValue(args, index, argument.includes('='));
233
+ if (consumed === null)
234
+ return null;
235
+ index = consumed;
236
+ }
237
+ continue;
238
+ }
239
+ if (!hasExplicitPattern && !implicitPatternSeen) {
240
+ implicitPatternSeen = true;
241
+ continue;
242
+ }
243
+ inputOperandSeen = true;
244
+ if (argument !== '-')
245
+ paths.push(recursiveAccess ? recursive(argument) : exact(argument));
246
+ }
247
+ if (!hasExplicitPattern && !implicitPatternSeen)
248
+ return null;
249
+ if (recursiveAccess && !inputOperandSeen)
250
+ paths.push(recursive('.'));
251
+ return paths;
252
+ }
253
+ function analyzeRg(args) {
254
+ if (args.some(argument => RG_PROCESS_SPAWNING_OPTION_RE.test(argument)
255
+ || argument === '--follow'
256
+ // `-z` is the short form of `--search-zip`; it may also appear in a
257
+ // short-option cluster such as `-nz`. Once -e/-f/-g starts an attached
258
+ // value, later letters belong to that value rather than the option set.
259
+ || (argument.startsWith('-') && !argument.startsWith('--') && (() => {
260
+ const body = argument.slice(1);
261
+ if (body.length > 1 && ['e', 'f', 'g'].includes(body[0]))
262
+ return false;
263
+ if (/[efg]/.test(body.slice(1)))
264
+ return true;
265
+ return /[Lz]/.test(body);
266
+ })())))
267
+ return null;
268
+ const paths = [];
269
+ let optionsEnded = false;
270
+ let hasExplicitPattern = false;
271
+ let implicitPatternSeen = false;
272
+ let filesMode = false;
273
+ let inputOperandSeen = false;
274
+ const textValueOptions = new Set([
275
+ '-A', '-B', '-C', '-E', '-M', '-g', '-j', '-m', '-r', '-t', '-T',
276
+ '--after-context', '--before-context', '--color', '--colors', '--context', '--context-separator',
277
+ '--dfa-size-limit', '--encoding', '--engine', '--field-context-separator', '--field-match-separator',
278
+ '--glob', '--iglob', '--max-columns', '--max-count', '--max-depth', '--max-filesize', '--path-separator',
279
+ '--regex-size-limit', '--replace', '--sort', '--sortr', '--type', '--type-add', '--type-clear', '--type-not',
280
+ ]);
281
+ const patternFileOptions = new Set(['-f', '--file']);
282
+ const auxiliaryFileOptions = new Set(['--ignore-file']);
283
+ for (let index = 0; index < args.length; index++) {
284
+ const argument = args[index];
285
+ if (!optionsEnded && argument === '--') {
286
+ optionsEnded = true;
287
+ continue;
288
+ }
289
+ if (!optionsEnded && argument.startsWith('-') && argument !== '-') {
290
+ const option = argument.split('=', 1)[0];
291
+ if (['--files', '--files-with-matches', '--files-without-match', '--type-list'].includes(option))
292
+ filesMode = true;
293
+ const inlineShort = !argument.startsWith('--') && argument.length > 2
294
+ && (argument[1] === 'e' || argument[1] === 'f' || argument[1] === 'g');
295
+ if (option === '-e' || option === '--regexp' || (!argument.startsWith('--') && argument.startsWith('-e'))) {
296
+ const consumed = consumeOptionValue(args, index, argument.includes('=') || inlineShort);
297
+ if (consumed === null)
298
+ return null;
299
+ index = consumed;
300
+ hasExplicitPattern = true;
301
+ continue;
302
+ }
303
+ const patternFileOption = patternFileOptions.has(option)
304
+ || (!argument.startsWith('--') && argument.startsWith('-f'));
305
+ const pathOption = patternFileOption || auxiliaryFileOptions.has(option);
306
+ if (pathOption) {
307
+ const inline = argument.includes('=') || inlineShort;
308
+ const consumed = consumeOptionValue(args, index, inline);
309
+ if (consumed === null)
310
+ return null;
311
+ if (inline) {
312
+ const value = argument.includes('=') ? argument.slice(argument.indexOf('=') + 1) : argument.slice(2);
313
+ if (value)
314
+ paths.push(exact(value));
315
+ }
316
+ else {
317
+ paths.push(exact(args[consumed]));
318
+ }
319
+ index = consumed;
320
+ if (patternFileOption)
321
+ hasExplicitPattern = true;
322
+ continue;
323
+ }
324
+ if (textValueOptions.has(option) || (!argument.startsWith('--') && argument.startsWith('-g'))) {
325
+ const consumed = consumeOptionValue(args, index, argument.includes('=') || inlineShort);
326
+ if (consumed === null)
327
+ return null;
328
+ index = consumed;
329
+ }
330
+ continue;
331
+ }
332
+ if (!filesMode && !hasExplicitPattern && !implicitPatternSeen) {
333
+ implicitPatternSeen = true;
334
+ continue;
335
+ }
336
+ inputOperandSeen = true;
337
+ if (argument !== '-')
338
+ paths.push(recursive(argument));
339
+ }
340
+ if (!filesMode && !hasExplicitPattern && !implicitPatternSeen)
341
+ return null;
342
+ if (!inputOperandSeen)
343
+ paths.push(recursive('.'));
344
+ return paths;
345
+ }
346
+ function analyzeFind(args) {
347
+ if (args.some(argument => FIND_MUTATING_PRIMARY_RE.test(argument)))
348
+ return null;
349
+ if (args.includes('-L') || args.includes('-H') || args.includes('-follow'))
350
+ return null;
351
+ const paths = [];
352
+ let index = 0;
353
+ while (index < args.length && (args[index] === '-P' || args[index].startsWith('-O') || args[index] === '-D')) {
354
+ if (args[index] === '-D') {
355
+ if (++index >= args.length)
356
+ return null;
357
+ }
358
+ index++;
359
+ }
360
+ while (index < args.length && !args[index].startsWith('-') && !['!', '(', ')'].includes(args[index])) {
361
+ paths.push(recursive(args[index++]));
362
+ }
363
+ if (paths.length === 0)
364
+ paths.push(recursive('.'));
365
+ const noValue = new Set([
366
+ '!', '(', ')', '-a', '-and', '-d', '-daystart', '-depth', '-empty', '-executable', '-false',
367
+ '-help', '--help', '-ignore_readdir_race', '-ls', '-mount', '-noignore_readdir_race', '-noleaf',
368
+ '-not', '-o', '-or', '-print', '-print0', '-prune', '-quit', '-readable', '-true', '-version',
369
+ '--version', '-writable', '-xdev',
370
+ ]);
371
+ const textValue = new Set([
372
+ '-amin', '-atime', '-cmin', '-ctime', '-fstype', '-gid', '-group', '-ilname', '-iname', '-inum',
373
+ '-ipath', '-iregex', '-iwholename', '-links', '-lname', '-maxdepth', '-mindepth', '-mmin', '-mtime',
374
+ '-name', '-path', '-perm', '-printf', '-regex', '-regextype', '-size', '-type', '-uid', '-used',
375
+ '-user', '-wholename', '-xtype',
376
+ ]);
377
+ const pathValue = new Set(['-anewer', '-cnewer', '-newer', '-samefile']);
378
+ while (index < args.length) {
379
+ const argument = args[index++];
380
+ // The file contents become additional starting points, so their protected
381
+ // path semantics cannot be proven from argv alone.
382
+ if (argument === '-files0-from')
383
+ return null;
384
+ if (noValue.has(argument))
385
+ continue;
386
+ if (textValue.has(argument)) {
387
+ if (index >= args.length)
388
+ return null;
389
+ index++;
390
+ continue;
391
+ }
392
+ if (/^-newer[aBcm]?t$/.test(argument)) {
393
+ if (index >= args.length)
394
+ return null;
395
+ index++;
396
+ continue;
397
+ }
398
+ if (/^-newer[A-Za-z]{2}$/.test(argument) || pathValue.has(argument)) {
399
+ if (index >= args.length)
400
+ return null;
401
+ paths.push(exact(args[index++]));
402
+ continue;
403
+ }
404
+ return null;
405
+ }
406
+ return paths;
407
+ }
408
+ function analyzeSed(args) {
409
+ if (!['-n', '--quiet', '--silent'].includes(args[0]))
410
+ return null;
411
+ const program = args[1];
412
+ if (!program || !/^(?:p|(?:\d+|\$)(?:,(?:\d+|\$))?p)$/.test(program))
413
+ return null;
414
+ if (args.slice(2).some(argument => argument.startsWith('-') && argument !== '-'))
415
+ return null;
416
+ return args.slice(2).filter(argument => argument !== '-').map(exact);
417
+ }
418
+ function analyzeHead(args) {
419
+ const parsed = parseOptionsAndPaths(args, new Set(['-c', '-n', '--bytes', '--lines']));
420
+ return parsed?.paths.map(exact) ?? null;
421
+ }
422
+ function analyzeSort(args) {
423
+ if (args.some(argument => /^(?:-o|-T)(?:.+|$)|^(?:--output|--compress-program|--temporary-directory|--files0-from)(?:=|$)/.test(argument))) {
424
+ return null;
425
+ }
426
+ const paths = [];
427
+ let optionsEnded = false;
428
+ const textValueOptions = new Set([
429
+ '-k', '-S', '-t', '--batch-size', '--buffer-size', '--field-separator', '--key', '--parallel', '--sort',
430
+ ]);
431
+ for (let index = 0; index < args.length; index++) {
432
+ const argument = args[index];
433
+ if (!optionsEnded && argument === '--') {
434
+ optionsEnded = true;
435
+ continue;
436
+ }
437
+ if (!optionsEnded && argument.startsWith('-') && argument !== '-') {
438
+ const option = argument.split('=', 1)[0];
439
+ if (option === '--random-source') {
440
+ const inline = argument.includes('=');
441
+ const consumed = consumeOptionValue(args, index, inline);
442
+ if (consumed === null)
443
+ return null;
444
+ paths.push(exact(inline ? argument.slice(argument.indexOf('=') + 1) : args[consumed]));
445
+ index = consumed;
446
+ continue;
447
+ }
448
+ if (textValueOptions.has(option)) {
449
+ const consumed = consumeOptionValue(args, index, argument.includes('='));
450
+ if (consumed === null)
451
+ return null;
452
+ index = consumed;
453
+ }
454
+ continue;
455
+ }
456
+ if (argument !== '-')
457
+ paths.push(exact(argument));
458
+ }
459
+ return paths;
460
+ }
461
+ function analyzeCommand(argv) {
462
+ const executable = normalizeExecutable(argv[0]);
463
+ if (!executable)
464
+ return null;
465
+ const args = argv.slice(1);
466
+ let pathOperands = null;
467
+ switch (executable) {
468
+ case 'echo':
469
+ pathOperands = [];
470
+ break;
471
+ case 'pwd':
472
+ pathOperands = args.every(argument => ['-L', '-P', '--help', '--version'].includes(argument)) ? [] : null;
473
+ break;
474
+ case 'ls':
475
+ pathOperands = analyzeLs(args);
476
+ break;
477
+ case 'cat':
478
+ pathOperands = analyzeCat(args);
479
+ break;
480
+ case 'grep':
481
+ pathOperands = analyzeGrep(args);
482
+ break;
483
+ case 'rg':
484
+ pathOperands = analyzeRg(args);
485
+ break;
486
+ case 'find':
487
+ pathOperands = analyzeFind(args);
488
+ break;
489
+ case 'sed':
490
+ pathOperands = analyzeSed(args);
491
+ break;
492
+ case 'head':
493
+ pathOperands = analyzeHead(args);
494
+ break;
495
+ case 'sort':
496
+ pathOperands = analyzeSort(args);
497
+ break;
498
+ }
499
+ if (!pathOperands)
500
+ return null;
501
+ return { command: { executable, argv, pathOperands } };
502
+ }
503
+ export function analyzeReadonlyShellQuery(command) {
504
+ const structure = splitQueryStructure(command);
505
+ if (!structure)
506
+ return { kind: 'unproven', reason: 'unsupported-shell-structure' };
507
+ const pipelines = [];
508
+ const pathOperands = [];
509
+ for (const stages of structure) {
510
+ const pipeline = [];
511
+ for (let index = 0; index < stages.length; index++) {
512
+ const argv = parseStageArgv(stages[index]);
513
+ if (!argv)
514
+ return { kind: 'unproven', reason: 'non-literal-command' };
515
+ const analyzed = analyzeCommand(argv);
516
+ if (!analyzed)
517
+ return { kind: 'unproven', reason: 'unsupported-command-or-option' };
518
+ if (index > 0) {
519
+ if (!['head', 'sort'].includes(analyzed.command.executable)) {
520
+ return { kind: 'unproven', reason: 'unsupported-pipeline-filter' };
521
+ }
522
+ if (analyzed.command.pathOperands.length > 0) {
523
+ return { kind: 'unproven', reason: 'pipeline-filter-must-use-stdin' };
524
+ }
525
+ }
526
+ pipeline.push(analyzed.command);
527
+ pathOperands.push(...analyzed.command.pathOperands);
528
+ }
529
+ pipelines.push(pipeline);
530
+ }
531
+ return { kind: 'proven-readonly', ir: { pipelines, pathOperands } };
532
+ }
@@ -210,7 +210,7 @@ function appendLClassReadOnlyBinds(args, root) {
210
210
  args.push('--ro-bind', target.path, target.path);
211
211
  }
212
212
  }
213
- export function buildHClassGuardCommand(executable, executableArgs, root = resolveRoot()) {
213
+ export function buildHClassGuardCommand(executable, executableArgs, root = resolveRoot(), options = {}) {
214
214
  const bubblewrapPath = resolveBubblewrapPath();
215
215
  if (!bubblewrapPath)
216
216
  return undefined;
@@ -224,6 +224,27 @@ export function buildHClassGuardCommand(executable, executableArgs, root = resol
224
224
  ];
225
225
  appendLClassReadOnlyBinds(args, path.resolve(root));
226
226
  appendHClassMasks(args, path.resolve(root), true);
227
+ if (options.managedCodexRequirements) {
228
+ const requirements = path.resolve(options.managedCodexRequirements);
229
+ if (!fs.statSync(requirements).isFile()) {
230
+ throw new Error(`Codex managed requirements is not a file: ${requirements}`);
231
+ }
232
+ const managedDirectory = path.dirname(requirements);
233
+ // Keep the copied hook immutable even when a session uses bypass mode.
234
+ args.push('--ro-bind', managedDirectory, managedDirectory);
235
+ // A host may not have /etc/codex yet. Overlay /etc inside this namespace so
236
+ // Bubblewrap can create the mountpoint without modifying the host, while
237
+ // retaining the complete host /etc as the read-only lower layer.
238
+ args.push('--overlay-src', '/etc');
239
+ args.push('--tmp-overlay', '/etc');
240
+ args.push('--dir', '/etc/codex');
241
+ // Replace the complete Codex system-config directory inside this app-server
242
+ // namespace so host system hooks cannot join EvolCore's managed hook set.
243
+ args.push('--tmpfs', '/etc/codex');
244
+ args.push('--ro-bind', requirements, '/etc/codex/requirements.toml');
245
+ args.push('--remount-ro', '/etc/codex');
246
+ args.push('--remount-ro', '/etc');
247
+ }
227
248
  args.push('--', executable, ...executableArgs);
228
249
  return { command: bubblewrapPath, args };
229
250
  }
@@ -0,0 +1,46 @@
1
+ const SHELL_CODE_INJECTION_ENV = new Set([
2
+ 'BASH_ENV',
3
+ 'BASHOPTS',
4
+ 'BASH_XTRACEFD',
5
+ 'CDPATH',
6
+ 'ENV',
7
+ 'GCONV_PATH',
8
+ 'GREP_OPTIONS',
9
+ 'LD_AUDIT',
10
+ 'LD_LIBRARY_PATH',
11
+ 'LD_PRELOAD',
12
+ 'LOCPATH',
13
+ 'NODE_OPTIONS',
14
+ 'NODE_PATH',
15
+ 'PROMPT_COMMAND',
16
+ 'PS4',
17
+ 'RIPGREP_CONFIG_PATH',
18
+ 'SHELLOPTS',
19
+ 'DYLD_FRAMEWORK_PATH',
20
+ 'DYLD_INSERT_LIBRARIES',
21
+ 'DYLD_LIBRARY_PATH',
22
+ ]);
23
+ const EXECUTABLE_RESOLUTION_ENV = ['PATH', 'HOME', 'SHELL'];
24
+ export function isShellCodeInjectionEnvironmentName(name) {
25
+ return SHELL_CODE_INJECTION_ENV.has(name)
26
+ || name.startsWith('BASH_FUNC_')
27
+ || name.startsWith('LD_')
28
+ || name.startsWith('DYLD_');
29
+ }
30
+ export function sanitizeShellExecutionEnvironment(input, options = {}) {
31
+ const sanitized = { ...input };
32
+ for (const name of Object.keys(sanitized)) {
33
+ if (isShellCodeInjectionEnvironmentName(name))
34
+ delete sanitized[name];
35
+ }
36
+ if (options.lockExecutableResolution) {
37
+ const baseline = options.baseline ?? process.env;
38
+ for (const name of EXECUTABLE_RESOLUTION_ENV) {
39
+ if (baseline[name] === undefined)
40
+ delete sanitized[name];
41
+ else
42
+ sanitized[name] = baseline[name];
43
+ }
44
+ }
45
+ return sanitized;
46
+ }