evolcore 0.0.35 → 0.0.36

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 (224) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +6 -0
  3. package/bin/install-codex-managed-hooks.mjs +20 -3
  4. package/dist/agents/baseagent-availability.js +1 -1
  5. package/dist/agents/baseagent.js +88 -0
  6. package/dist/agents/claude-runner.js +59 -12
  7. package/dist/agents/codex/app-server-client.js +18 -1
  8. package/dist/agents/codex-runner.js +262 -21
  9. package/dist/agents/ecagent-runner.js +47 -11
  10. package/dist/agents/gemini-runner.js +36 -12
  11. package/dist/aun/aid/client.js +4 -3
  12. package/dist/aun/aid/control-aid.js +270 -8
  13. package/dist/aun/aid/identity.js +6 -3
  14. package/dist/aun/aid/ownership-operation.js +58 -0
  15. package/dist/aun/aid/store.js +1 -0
  16. package/dist/aun/group/fs-download.js +67 -2
  17. package/dist/aun/group/history.js +20 -5
  18. package/dist/aun/group/operations.js +8 -2
  19. package/dist/aun/menu/client.js +39 -2
  20. package/dist/aun/menu/transport.js +1 -1
  21. package/dist/aun/msg/history.js +43 -4
  22. package/dist/aun/msg/managed-operation.js +30 -53
  23. package/dist/aun/msg/p2p.js +51 -15
  24. package/dist/aun/outbox.js +4 -0
  25. package/dist/channels/aun.js +260 -73
  26. package/dist/channels/daemon.js +6 -4
  27. package/dist/channels/feishu.js +3 -0
  28. package/dist/cli/agent-command.js +161 -223
  29. package/dist/cli/agent-create.js +190 -223
  30. package/dist/cli/agent.js +3 -1
  31. package/dist/cli/aid-bind-command.js +53 -0
  32. package/dist/cli/aun-commands.js +384 -91
  33. package/dist/cli/bench.js +1 -1
  34. package/dist/cli/cli-argv.js +74 -0
  35. package/dist/cli/daemon-commands.js +105 -74
  36. package/dist/cli/diag.js +1 -1
  37. package/dist/cli/fs-command.js +18 -19
  38. package/dist/cli/getmeta-command.js +159 -6
  39. package/dist/cli/index.js +6 -12
  40. package/dist/cli/init-channel.js +8 -55
  41. package/dist/cli/init.js +125 -218
  42. package/dist/cli/queue-command.js +11 -8
  43. package/dist/cli/restart-monitor.js +134 -48
  44. package/dist/cli/session-command.js +1 -1
  45. package/dist/cli/stats.js +7 -5
  46. package/dist/cli/thread-command.js +103 -30
  47. package/dist/cli/trigger-command.js +1 -1
  48. package/dist/cli/watch-msg-diag.js +1 -0
  49. package/dist/config/access-policy.js +2 -2
  50. package/dist/config/boot-log.js +14 -11
  51. package/dist/config/config-field-policy.js +9 -1
  52. package/dist/config/config-manager.js +176 -90
  53. package/dist/config/config-operation-service.js +7 -14
  54. package/dist/config/contact-book-store.js +142 -26
  55. package/dist/config/contact-book.js +4 -2
  56. package/dist/config/lifecycle.js +1 -1
  57. package/dist/config/owner-config.js +96 -0
  58. package/dist/config/peer-role-resolver.js +12 -18
  59. package/dist/config/providers-config.js +382 -0
  60. package/dist/config/providers-import.js +74 -0
  61. package/dist/config/providers-management.js +328 -0
  62. package/dist/config/providers.js +22 -0
  63. package/dist/config/resolved-config-op.js +17 -14
  64. package/dist/config/snapshot.js +28 -16
  65. package/dist/config-store.js +18 -14
  66. package/dist/core/agent-ipc-registration.js +21 -63
  67. package/dist/core/agent-runtime.js +16 -5
  68. package/dist/core/auth/agent-delegation.js +199 -25
  69. package/dist/core/auth/auth-gateway.js +44 -2
  70. package/dist/core/auth/authorization-denial.js +1 -1
  71. package/dist/core/auth/debug-context.js +2 -1
  72. package/dist/core/auth/delegation-authorization.js +2 -2
  73. package/dist/core/auth/delegation-diagnostics.js +4 -1
  74. package/dist/core/auth/operation-authorizer.js +10 -1
  75. package/dist/core/auth/operation-catalog.js +19 -1
  76. package/dist/core/bootstrap-service.js +43 -20
  77. package/dist/core/channel-loader.js +0 -1
  78. package/dist/core/channel-runtime.js +30 -4
  79. package/dist/core/command/agent-control.js +196 -37
  80. package/dist/core/command/cli-intent-parser.js +60 -4
  81. package/dist/core/command/command-handler.js +212 -60
  82. package/dist/core/command/connect-menu.js +1 -0
  83. package/dist/core/command/diag-service.js +5 -4
  84. package/dist/core/command/file-search.js +368 -0
  85. package/dist/core/command/menu-catalog.js +2 -1
  86. package/dist/core/command/menu-handler.js +292 -107
  87. package/dist/core/command/menu-protocol.js +23 -0
  88. package/dist/core/command/role-menu.js +7 -19
  89. package/dist/core/command/skill-slash.js +130 -25
  90. package/dist/core/command/slash-handler.js +101 -53
  91. package/dist/core/command/topic-slash.js +16 -5
  92. package/dist/core/daemon-bootstrap.js +7 -1
  93. package/dist/core/daemon-control.js +19 -1
  94. package/dist/core/daemon-lifecycle.js +2 -1
  95. package/dist/core/event-catalog.js +2 -0
  96. package/dist/core/evolagent-registry.js +13 -2
  97. package/dist/core/evolagent.js +3 -6
  98. package/dist/core/handoff/ipc-registration.js +2 -2
  99. package/dist/core/handoff/runtime.js +40 -6
  100. package/dist/core/handoff/store.js +1 -0
  101. package/dist/core/inference/text-inference.js +95 -6
  102. package/dist/core/ipc-registration.js +43 -24
  103. package/dist/core/memory/consolidation-context.js +4 -1
  104. package/dist/core/memory/consolidation.js +25 -14
  105. package/dist/core/message/aun-ipc-sender.js +296 -88
  106. package/dist/core/message/aun-request-registry.js +226 -0
  107. package/dist/core/message/create-status.js +33 -4
  108. package/dist/core/message/message-bridge.js +62 -13
  109. package/dist/core/message/message-log.js +0 -1
  110. package/dist/core/message/message-queue.js +4 -1
  111. package/dist/core/message/message-utils.js +8 -1
  112. package/dist/core/message/peer-mode.js +17 -9
  113. package/dist/core/message/response-engine.js +266 -124
  114. package/dist/core/message/retry-scheduler.js +11 -8
  115. package/dist/core/model/catalog-service.js +60 -18
  116. package/dist/core/model/model-catalog.js +112 -9
  117. package/dist/core/model/model-fallback.js +94 -26
  118. package/dist/core/ownership/owner-resolver.js +687 -0
  119. package/dist/core/permission/approval-gateway.js +196 -62
  120. package/dist/core/permission/ec-command-parser.js +138 -25
  121. package/dist/core/permission/filesystem-approval-scope.js +141 -0
  122. package/dist/core/permission/protected-paths.js +14 -1
  123. package/dist/core/permission/sandbox-runtime.js +171 -18
  124. package/dist/core/permission/tool-policy.js +13 -2
  125. package/dist/core/role/runtime-policy.js +3 -4
  126. package/dist/core/session/session-idle-monitor.js +10 -8
  127. package/dist/core/session/session-manager.js +72 -45
  128. package/dist/core/session/session-mapper.js +18 -7
  129. package/dist/core/session/session-renew.js +5 -4
  130. package/dist/core/session/session-title.js +2 -1
  131. package/dist/core/session/session-turn-coordinator.js +4 -2
  132. package/dist/core/skill/index-generator.js +2 -16
  133. package/dist/core/task/task-recovery-service.js +15 -2
  134. package/dist/core/thread/command-parser.js +28 -13
  135. package/dist/core/thread/index.js +1 -0
  136. package/dist/core/thread/ipc-operation.js +11 -8
  137. package/dist/core/thread/thread-operation.js +79 -44
  138. package/dist/core/thread/thread-route.js +54 -6
  139. package/dist/core/thread/thread-service.js +236 -45
  140. package/dist/core/thread/topic-id.js +248 -0
  141. package/dist/eck/kit-renderer.js +1 -1
  142. package/dist/eck/message-renderer.js +12 -2
  143. package/dist/index.js +50 -27
  144. package/dist/ipc.js +59 -6
  145. package/dist/paths.js +1 -0
  146. package/dist/product.js +1 -0
  147. package/dist/response-system/engines/v1/proactive-flow.js +10 -3
  148. package/dist/stats/db.js +8 -0
  149. package/dist/stats/writer.js +26 -4
  150. package/dist/trigger/authorization.js +48 -3
  151. package/dist/trigger/feedback.js +28 -10
  152. package/dist/trigger/ipc-service.js +86 -10
  153. package/dist/trigger/local-files.js +55 -23
  154. package/dist/trigger/scheduler.js +57 -11
  155. package/dist/trigger/system-definitions.js +1 -0
  156. package/dist/trigger/validation.js +44 -15
  157. package/dist/utils/aid-bind.js +17 -297
  158. package/dist/utils/avatar-upload.js +16 -4
  159. package/dist/utils/cross-platform.js +109 -34
  160. package/dist/utils/ecweb-supervisor.js +1 -1
  161. package/dist/utils/ecweb-utils.js +8 -1
  162. package/dist/utils/error-utils.js +44 -6
  163. package/dist/utils/instance-registry.js +12 -2
  164. package/dist/utils/npm-ops.js +102 -27
  165. package/dist/utils/package-version-info.js +72 -27
  166. package/dist/utils/restart-safety.js +50 -0
  167. package/dist/utils/upgrade-jobs.js +304 -0
  168. package/dist/utils/windows-autostart.js +17 -10
  169. package/kits/docs/INDEX.md +5 -0
  170. package/kits/docs/aun/ABILITY_CALLING.md +239 -0
  171. package/kits/docs/evolcore/INDEX.md +14 -3
  172. package/kits/docs/evolcore/agent-create.md +55 -141
  173. package/kits/docs/evolcore/agent.md +38 -16
  174. package/kits/docs/evolcore/aid.md +34 -0
  175. package/kits/docs/evolcore/config.md +20 -1
  176. package/kits/docs/evolcore/event.md +2 -2
  177. package/kits/docs/evolcore/fs.md +18 -2
  178. package/kits/docs/evolcore/group-collaboration.md +13 -246
  179. package/kits/docs/evolcore/group.md +2 -1
  180. package/kits/docs/evolcore/memory-consolidation-batches.md +1 -1
  181. package/kits/docs/evolcore/menu-protocol.md +65 -1
  182. package/kits/docs/evolcore/msg.md +64 -2
  183. package/kits/docs/evolcore/skill.md +86 -0
  184. package/kits/docs/evolcore/stats.md +6 -0
  185. package/kits/docs/evolcore/task.md +2 -0
  186. package/kits/docs/evolcore/thread-chain.md +145 -51
  187. package/kits/docs/evolcore/trigger.md +48 -3
  188. package/kits/eck_manifest.json +21 -0
  189. package/kits/eck_message_manifest.json +4 -4
  190. package/kits/migrations/migrate-role-config-v5.mjs +20 -7
  191. package/kits/rules/01-overview.md +2 -1
  192. package/kits/rules/03-identity.md +3 -1
  193. package/kits/rules/04-relation.md +1 -1
  194. package/kits/rules/07-memory-and-thread.md +1 -1
  195. package/kits/schemas/_meta.json +16 -1
  196. package/kits/schemas/agent-config.schema.13.json +44 -2
  197. package/kits/schemas/contact-book.schema.3.json +5 -0
  198. package/kits/schemas/daemon.schema.6.json +3 -1
  199. package/kits/schemas/daemon.schema.8.json +329 -0
  200. package/kits/schemas/defaults.schema.5.json +39 -3
  201. package/kits/schemas/providers.schema.1.json +76 -0
  202. package/kits/schemas/relation-config.schema.9.json +5 -1
  203. package/kits/skills/.switches.json +28 -0
  204. package/kits/skills/aid-ability/SKILL.md +214 -0
  205. package/kits/skills/ec-agent-create/SKILL.md +162 -0
  206. package/kits/skills/ec-group/SKILL.md +2 -1
  207. package/kits/skills/ec-group-collaboration/SKILL.md +228 -0
  208. package/kits/skills/ec-identity/SKILL.md +143 -0
  209. package/kits/skills/ec-skill/scripts/cli.mjs +36 -3
  210. package/kits/skills/ec-skill/scripts/skill.mjs +202 -15
  211. package/kits/skills/ec-trigger/SKILL.md +167 -0
  212. package/kits/skills/ec-trigger/references/examples.md +153 -0
  213. package/kits/skills/thread-ops/SKILL.md +108 -420
  214. package/kits/templates/message-fragments/thread-context.md +1 -2
  215. package/kits/templates/message-fragments/trigger-thread-check.md +1 -1
  216. package/kits/templates/roles/member.json +1 -1
  217. package/kits/templates/roles/visitor.json +1 -0
  218. package/kits/templates/system-fragments/bootstrap.md +1 -0
  219. package/kits/templates/system-fragments/channel.md +2 -1
  220. package/kits/templates/system-fragments/commands.md +5 -4
  221. package/kits/templates/system-fragments/identity.md +2 -0
  222. package/kits/templates/system-fragments/session.md +1 -1
  223. package/kits/templates/system-fragments/venue.md +1 -1
  224. package/package.json +4 -3
@@ -1,14 +1,18 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
+ import { execFileSync } from 'node:child_process';
3
4
  import { resolveCommandPath } from '../../utils/cross-platform.js';
4
5
  import { getExistingHClassMaskTargets, getExistingRelationAttachmentDirs, getExistingLClassReadOnlyTargets, isSameOrDescendant, } from './protected-paths.js';
5
6
  import { resolveInstanceSocketPath, resolveRoot } from '../../paths.js';
6
- let cachedBubblewrapPath;
7
+ import { MAIN_CLI_BIN } from '../../product.js';
7
8
  let sandboxInitializationCircuit;
8
9
  const sessionSandboxInitializationCircuits = new Map();
9
10
  let sandboxCircuitNoticeAt = 0;
10
11
  let sandboxInitializationFailureCounters = emptySandboxInitializationFailureCounters();
11
12
  export const SANDBOX_INITIALIZATION_FAILED = 'sandbox_initialization_failed';
13
+ export const SANDBOX_DEPENDENCIES_MISSING = 'sandbox_dependencies_missing';
14
+ export const CODEX_SANDBOX_USER_NAMESPACE_UNAVAILABLE = 'codex_sandbox_userns_unavailable';
15
+ export const CODEX_SANDBOX_USER_NAMESPACE_DISABLED_BY_APPARMOR = 'codex_sandbox_userns_disabled_by_apparmor';
12
16
  export const SANDBOX_INITIALIZATION_COOLDOWN_MS = 5 * 60 * 1000;
13
17
  export const SANDBOX_CIRCUIT_NOTICE_INTERVAL_MS = 60 * 1000;
14
18
  function emptySandboxInitializationFailureCounters() {
@@ -26,7 +30,7 @@ function sandboxInitializationFailureKind(text) {
26
30
  return 'gid_map';
27
31
  if (/\bseccomp\b/i.test(text))
28
32
  return 'seccomp';
29
- if (/user namespace/i.test(text))
33
+ if (isCodexSandboxUserNamespaceFailure(text))
30
34
  return 'user_namespace';
31
35
  if (/\b(?:bwrap|bubblewrap)\b/i.test(text))
32
36
  return 'bubblewrap';
@@ -101,13 +105,34 @@ export function isSandboxInitializationFailure(error, stderr = []) {
101
105
  .join('\n')
102
106
  .toLowerCase();
103
107
  return text.includes(SANDBOX_INITIALIZATION_FAILED)
108
+ || (text.includes('sandbox required but unavailable') && !isClaudeSandboxDependencyFailure(text))
104
109
  || /fs sandbox helper failed(?: with status)?\b/.test(text)
105
110
  || /(?:apply-seccomp|seccomp)[\s\S]*(?:uid_map|gid_map|operation not permitted)/.test(text)
106
111
  || /(?:uid_map|gid_map)[\s\S]*(?:operation not permitted|permission denied)/.test(text)
107
- || /user namespace[\s\S]*(?:not permitted|unavailable|failed)/.test(text)
112
+ || isCodexSandboxUserNamespaceFailure(text)
108
113
  || /bubblewrap[\s\S]*namespace[\s\S]*(?:failed|not permitted|permission denied)/.test(text)
109
114
  || /(?:^|\n)\s*bwrap:\s*(?:can't|cannot|unable to)[\s\S]*(?:bind|mount|mount table)/.test(text)
110
- || /(?:^|\n)\s*bwrap:\s*(?:can't|cannot) create (?:file|directory) at [^\n]+: read-only file system/.test(text);
115
+ || /(?:^|\n)\s*bwrap:\s*(?:can't|cannot) create (?:file|directory) at [^\n]+: read-only file system/.test(text)
116
+ || /(?:^|\n)\s*bwrap:\s*(?:can't|cannot) mkdir parents for [^\n]+: read-only file system/.test(text);
117
+ }
118
+ export function isCodexSandboxUserNamespaceFailure(error) {
119
+ const record = error && typeof error === 'object'
120
+ ? error
121
+ : undefined;
122
+ const stderr = Array.isArray(record?.stderr)
123
+ ? record.stderr.filter((line) => typeof line === 'string').join('\n')
124
+ : typeof record?.stderr === 'string' ? record.stderr : '';
125
+ const text = record
126
+ ? `${String(record.code ?? '')}\n${String(record.message ?? '')}\n${stderr}`
127
+ : String(error ?? '');
128
+ const lower = text.toLowerCase();
129
+ return lower.includes(CODEX_SANDBOX_USER_NAMESPACE_UNAVAILABLE)
130
+ || lower.includes(CODEX_SANDBOX_USER_NAMESPACE_DISABLED_BY_APPARMOR)
131
+ || /apparmor_restrict_unprivileged_userns\s*=\s*1/i.test(text)
132
+ || /no permissions to create new namespace/i.test(text)
133
+ || /(?:new )?namespace[^\n]*(?:operation not permitted|permission denied)/i.test(text)
134
+ || /non-privileged user namespaces?/i.test(text)
135
+ || /user namespace[\s\S]*(?:not permitted|unavailable|failed|permission denied)/i.test(text);
111
136
  }
112
137
  export function createSandboxInitializationError(error, stderr = []) {
113
138
  if (error instanceof Error
@@ -177,10 +202,10 @@ function isExecutableFile(candidate) {
177
202
  return false;
178
203
  }
179
204
  }
180
- function codexBundledBubblewrap() {
205
+ function codexBundledBubblewrap(env) {
181
206
  if (process.platform !== 'linux')
182
207
  return undefined;
183
- const codexPath = resolveCommandPath('codex');
208
+ const codexPath = resolveCommandPath('codex', env);
184
209
  if (!codexPath)
185
210
  return undefined;
186
211
  let resolvedCodexPath = codexPath;
@@ -199,21 +224,150 @@ function codexBundledBubblewrap() {
199
224
  const platformPackage = process.arch === 'arm64' ? 'codex-linux-arm64' : 'codex-linux-x64';
200
225
  return path.join(packageRoot, 'node_modules', '@openai', platformPackage, 'vendor', target, 'codex-resources', 'bwrap');
201
226
  }
202
- export function resolveBubblewrapPath() {
203
- if (cachedBubblewrapPath !== undefined)
204
- return cachedBubblewrapPath ?? undefined;
205
- if (process.platform !== 'linux') {
206
- cachedBubblewrapPath = null;
227
+ export function resolveBubblewrapPath(env = process.env) {
228
+ if (process.platform !== 'linux')
207
229
  return undefined;
208
- }
209
230
  const candidates = [
210
- process.env.EVOLCORE_BWRAP_PATH,
211
- resolveCommandPath('bwrap') ?? undefined,
212
- codexBundledBubblewrap(),
231
+ env.EVOLCORE_BWRAP_PATH,
232
+ resolveCommandPath('bwrap', env) ?? undefined,
233
+ codexBundledBubblewrap(env),
213
234
  ];
214
235
  const resolved = candidates.find(isExecutableFile);
215
- cachedBubblewrapPath = resolved ?? null;
216
- return resolved;
236
+ return resolved ? path.resolve(resolved) : undefined;
237
+ }
238
+ export function inspectClaudeSandboxDependencies(env = process.env, platform = process.platform) {
239
+ if (platform !== 'linux')
240
+ return { platform, status: 'not_applicable', missing: [] };
241
+ const bwrapPath = resolveBubblewrapPath(env);
242
+ const socatCandidate = resolveCommandPath('socat', env) ?? undefined;
243
+ const socatPath = isExecutableFile(socatCandidate) ? socatCandidate : undefined;
244
+ const missing = [...(bwrapPath ? [] : ['bubblewrap (bwrap)']), ...(socatPath ? [] : ['socat'])];
245
+ return { platform, status: missing.length ? 'missing' : 'ready', bwrapPath, socatPath, missing };
246
+ }
247
+ export function formatClaudeSandboxDependencies(check) {
248
+ if (check.status === 'not_applicable')
249
+ return `Claude Linux sandbox dependency check not applicable on ${check.platform}`;
250
+ if (check.status === 'ready')
251
+ return `Claude sandbox dependencies found: bwrap=${check.bwrapPath}; socat=${check.socatPath} (execution not verified)`;
252
+ return `Claude sandbox dependencies missing: ${check.missing.join(', ')}. `
253
+ + 'Sandbox-required sessions cannot execute; owner bypass may still work. '
254
+ + 'Install with your system package manager (Debian/Ubuntu: apt install bubblewrap socat; Fedora: dnf install bubblewrap socat), '
255
+ + `then run ${MAIN_CLI_BIN} diagnose in the service environment. EvolCore does not install OS packages or disable the sandbox automatically.`;
256
+ }
257
+ export function createClaudeSandboxDependencyError(check) {
258
+ return Object.assign(new Error(`${SANDBOX_DEPENDENCIES_MISSING}: ${formatClaudeSandboxDependencies(check)}`), {
259
+ code: SANDBOX_DEPENDENCIES_MISSING,
260
+ });
261
+ }
262
+ export function isClaudeSandboxDependencyFailure(error) {
263
+ const text = error && typeof error === 'object' && 'message' in error
264
+ ? String(error.message ?? '')
265
+ : String(error ?? '');
266
+ return text.includes(SANDBOX_DEPENDENCIES_MISSING)
267
+ || /sandbox[\s\S]*(?:dependencies are missing|dependencies missing|(?:socat|bwrap|bubblewrap) not installed)/i.test(text);
268
+ }
269
+ export function probeClaudeSandboxDependencies(check) {
270
+ if (check.status !== 'ready' || !check.bwrapPath)
271
+ return { ok: false, detail: formatClaudeSandboxDependencies(check) };
272
+ try {
273
+ execFileSync(check.bwrapPath, [
274
+ '--die-with-parent', '--new-session', '--unshare-pid', '--ro-bind', '/', '/',
275
+ '--proc', '/proc', '--dev', '/dev', '--', '/bin/true',
276
+ ], { timeout: 5_000, stdio: 'pipe' });
277
+ execFileSync(check.socatPath, ['-V'], { timeout: 5_000, stdio: 'pipe' });
278
+ return { ok: true, detail: 'Basic bwrap namespace and socat launch probes passed; full Claude sandbox policy is verified at execution.' };
279
+ }
280
+ catch (error) {
281
+ const failure = error;
282
+ return { ok: false, detail: `${SANDBOX_INITIALIZATION_FAILED}: ${failure.message}\n${failure.stderr?.toString() ?? ''}`.trim() };
283
+ }
284
+ }
285
+ function codexAppArmorRestrictsUnprivilegedUserns(platform) {
286
+ if (platform !== 'linux')
287
+ return undefined;
288
+ try {
289
+ return fs.readFileSync('/proc/sys/kernel/apparmor_restrict_unprivileged_userns', 'utf8').trim() === '1';
290
+ }
291
+ catch {
292
+ return undefined;
293
+ }
294
+ }
295
+ /** Resolve the bwrap binary Codex is most likely to use for its inner sandbox. */
296
+ export function resolveCodexSandboxBubblewrapPath(env = process.env) {
297
+ if (process.platform !== 'linux')
298
+ return undefined;
299
+ const bundled = codexBundledBubblewrap(env);
300
+ if (bundled && isExecutableFile(bundled))
301
+ return path.resolve(bundled);
302
+ return resolveBubblewrapPath(env);
303
+ }
304
+ export function inspectCodexSandboxAvailability(env = process.env, platform = process.platform) {
305
+ if (platform !== 'linux')
306
+ return { platform, status: 'not_applicable', missing: [] };
307
+ const bwrapPath = resolveCodexSandboxBubblewrapPath(env);
308
+ return {
309
+ platform,
310
+ status: bwrapPath ? 'ready' : 'missing',
311
+ bwrapPath,
312
+ apparmorRestrictsUnprivilegedUserns: codexAppArmorRestrictsUnprivilegedUserns(platform),
313
+ missing: bwrapPath ? [] : ['bubblewrap (bwrap)'],
314
+ };
315
+ }
316
+ export function formatCodexSandboxAvailability(check) {
317
+ if (check.status === 'not_applicable')
318
+ return `Codex Linux sandbox check not applicable on ${check.platform}`;
319
+ if (check.status === 'missing') {
320
+ return 'Codex Linux sandbox is unavailable: bubblewrap (bwrap) was not found; '
321
+ + 'install or provision the Codex sandbox runtime. EvolCore will not disable the sandbox automatically.';
322
+ }
323
+ const apparmor = check.apparmorRestrictsUnprivilegedUserns === true
324
+ ? ' AppArmor restricts unprivileged user namespaces on this host; the probe below confirms whether the active profile permits Codex.'
325
+ : '';
326
+ return `Codex Linux sandbox runtime found: bwrap=${check.bwrapPath}.${apparmor}`;
327
+ }
328
+ export function probeCodexSandboxAvailability(check) {
329
+ if (check.status !== 'ready' || !check.bwrapPath) {
330
+ return { ok: false, detail: formatCodexSandboxAvailability(check) };
331
+ }
332
+ try {
333
+ execFileSync(check.bwrapPath, [
334
+ '--die-with-parent', '--new-session', '--unshare-pid', '--ro-bind', '/', '/',
335
+ '--proc', '/proc', '--dev-bind', '/dev', '/dev', '--tmpfs', '/tmp',
336
+ '--', '/bin/true',
337
+ ], { timeout: 5_000, stdio: 'pipe' });
338
+ return { ok: true, detail: `Codex Linux sandbox user-namespace probe passed: bwrap=${check.bwrapPath}` };
339
+ }
340
+ catch (error) {
341
+ const failure = error;
342
+ const stderr = failure.stderr?.toString().trim();
343
+ const stdout = failure.stdout?.toString().trim();
344
+ const raw = stderr || stdout || failure.message.trim();
345
+ const apparmor = check.apparmorRestrictsUnprivilegedUserns === true
346
+ || /apparmor_restrict_unprivileged_userns\s*=\s*1|apparmor/i.test(raw);
347
+ if (isCodexSandboxUserNamespaceFailure(raw)) {
348
+ const code = apparmor
349
+ ? CODEX_SANDBOX_USER_NAMESPACE_DISABLED_BY_APPARMOR
350
+ : CODEX_SANDBOX_USER_NAMESPACE_UNAVAILABLE;
351
+ return {
352
+ ok: false,
353
+ kind: apparmor ? 'apparmor' : 'user_namespace',
354
+ detail: `${code}: ${raw}`,
355
+ raw,
356
+ };
357
+ }
358
+ return { ok: false, kind: 'bubblewrap', detail: `${SANDBOX_INITIALIZATION_FAILED}: ${raw}`, raw };
359
+ }
360
+ }
361
+ export function createCodexSandboxAvailabilityError(probe) {
362
+ const code = probe.kind === 'apparmor'
363
+ ? CODEX_SANDBOX_USER_NAMESPACE_DISABLED_BY_APPARMOR
364
+ : probe.kind === 'user_namespace'
365
+ ? CODEX_SANDBOX_USER_NAMESPACE_UNAVAILABLE
366
+ : SANDBOX_INITIALIZATION_FAILED;
367
+ return Object.assign(new Error(`${code}: ${probe.raw ?? probe.detail}`), {
368
+ code,
369
+ ...(probe.raw ? { raw: probe.raw } : {}),
370
+ });
217
371
  }
218
372
  export function prependExecutableDirectory(env, executablePath) {
219
373
  const currentPath = env.PATH ?? process.env.PATH ?? '';
@@ -713,7 +867,6 @@ export function buildBubblewrapCommand(executable, executableArgs, options) {
713
867
  }
714
868
  export function _resetSandboxRuntimeCache() {
715
869
  sessionSandboxInitializationCircuits.clear();
716
- cachedBubblewrapPath = undefined;
717
870
  sandboxInitializationCircuit = undefined;
718
871
  sandboxCircuitNoticeAt = 0;
719
872
  sandboxInitializationFailureCounters = emptySandboxInitializationFailureCounters();
@@ -1,4 +1,5 @@
1
1
  import fs from 'fs';
2
+ import { parseOwnershipCommand } from '../../aun/aid/ownership-operation.js';
2
3
  import path from 'path';
3
4
  import os from 'os';
4
5
  import { randomUUID } from 'crypto';
@@ -42,7 +43,7 @@ export function expandManagedTempPath(value, options = {}) {
42
43
  return substituteTmpDir(value, resolveManagedTempDir(options.env));
43
44
  }
44
45
  const WRITE_INTENT_RE = /(?:^|[\s;&|])(?:mkdir|mktemp|touch|tee|cp|mv|install|chmod|chown|ln|rm)(?:[\s;&|]|$)|(?:^|[\s])(?:\d>>?|>>?)\s*/i;
45
- const TEMP_TOKEN_RE = /(?:\$TMPDIR|\$\{TMPDIR\}|\/tmp(?:\/|$)|\/var\/tmp(?:\/|$)|\/dev\/shm(?:\/|$))[^\s'";&|<>)]*/g;
46
+ const TEMP_TOKEN_RE = /(?:\$TMPDIR|\$\{TMPDIR\}|\/(?:[^\s'";&|<>)]*\/)?(?:tmp|var\/tmp|dev\/shm)(?:\/|$))[^\s'";&|<>)]*/g;
46
47
  function windowsTempRoots(options) {
47
48
  if (process.platform !== 'win32')
48
49
  return [];
@@ -2394,7 +2395,7 @@ export function evaluateToolPreflight(toolName, input, context) {
2394
2395
  : undefined;
2395
2396
  const stringShellCarrier = shellCarrier
2396
2397
  ?? (explicitCommandArgv ? undefined : resolveCodexShellCarrierString(command));
2397
- const wrappedCommand = stringShellCarrier?.command;
2398
+ const wrappedCommand = stringShellCarrier?.parseFailure ? undefined : stringShellCarrier?.command;
2398
2399
  const policyCommand = wrappedCommand ?? commandForPolicy;
2399
2400
  if (context.sessionId && /^\s*(?:command\s+-v|which|type)\s+ec\s*$/i.test(policyCommand)) {
2400
2401
  return {
@@ -2553,7 +2554,17 @@ export function evaluateToolPreflight(toolName, input, context) {
2553
2554
  && ['request', 'bypass'].includes(context.permissionMode ?? '');
2554
2555
  const canUploadSelfAvatar = isSelfAvatarUpload
2555
2556
  && ['request', 'bypass'].includes(context.permissionMode ?? '');
2557
+ let isManagedOwnership = false;
2558
+ if (managedAidArgs?.[0] === 'bind') {
2559
+ try {
2560
+ const ownership = parseOwnershipCommand(managedAidArgs);
2561
+ isManagedOwnership = ownership.action === 'status'
2562
+ || ['request', 'bypass'].includes(context.permissionMode ?? '');
2563
+ }
2564
+ catch { }
2565
+ }
2556
2566
  const isAllowedManagedAidCommand = isManagedAidPublicRead
2567
+ || isManagedOwnership
2557
2568
  || isSelfManagedAidShow
2558
2569
  || canPublishSelfAgentMd
2559
2570
  || canSetSelfAgentMd
@@ -2,7 +2,7 @@ import { getRoleDefinition } from '../../config/roles.js';
2
2
  import { PUBLIC_PERMISSION_MODES } from '../permission/mode.js';
3
3
  import { ConfigTarget, read } from '../../config/config-manager.js';
4
4
  import { resolvePrincipal } from '../../config/contact-book.js';
5
- import { loadDaemonConfig } from '../../config-store.js';
5
+ import { getDaemonOwners, ownerResolver } from '../ownership/owner-resolver.js';
6
6
  import { hasTrustedPrincipal } from '../auth/authenticated-actor.js';
7
7
  const PERMISSION_MODE_AUTHORITY = {
8
8
  readonly: 0,
@@ -150,9 +150,8 @@ export function validateRuntimeStringFieldOverride(input) {
150
150
  export function isAgentFullAccessOwner(agentAid, principalId) {
151
151
  if (!principalId)
152
152
  return false;
153
- const config = read(ConfigTarget.Agent, { self: agentAid }, { cache: false });
154
- return config?.owners?.includes(principalId) === true
155
- && loadDaemonConfig().owners?.includes(principalId) === true;
153
+ return ownerResolver.isOwner(agentAid, principalId)
154
+ && getDaemonOwners().includes(principalId);
156
155
  }
157
156
  export function agentFullAccessStatus(agentAid, principalId) {
158
157
  const config = read(ConfigTarget.Agent, { self: agentAid }, { cache: false });
@@ -2,6 +2,7 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { ConfigTarget, mutateConfig, read as readConfig } from '../../config/config-manager.js';
4
4
  import { formatPeerKey } from '../relation/peer-identity.js';
5
+ import { isMainSession, isTopicSession, sessionTopicThreadId } from '../thread/topic-id.js';
5
6
  import { isSystemControlChannel } from '../system-channels.js';
6
7
  import { logger } from '../../utils/logger.js';
7
8
  import { atomicWriteJson } from './session-fs-store.js';
@@ -35,7 +36,7 @@ export function resolveSessionLoopTarget(session) {
35
36
  if (!session.selfAID || !peerId)
36
37
  return undefined;
37
38
  const peerKey = formatPeerKey(session.channelType || session.channel, peerId);
38
- return { level: session.threadId ? 'topic-session' : 'relation-main', session, peerKey };
39
+ return { level: isTopicSession(session) ? 'topic-session' : 'relation-main', session, peerKey };
39
40
  }
40
41
  export function readSessionLoopConfig(target) {
41
42
  let value;
@@ -90,7 +91,7 @@ export async function resolveCurrentSessionLoopTarget(store, route) {
90
91
  const target = resolveSessionLoopTarget(session);
91
92
  if (!target || !route.selfAid || session.selfAID !== route.selfAid
92
93
  || session.channel !== route.channel || session.channelId !== route.channelId
93
- || (session.threadId || '') !== (route.threadId || '')) {
94
+ || (sessionTopicThreadId(session) ?? '') !== (route.threadId || '')) {
94
95
  throw sessionLoopError('INVALID_CONTEXT', '当前会话路由或归属不匹配');
95
96
  }
96
97
  return target;
@@ -108,7 +109,7 @@ export function writeSessionLoopConfig(store, target, config) {
108
109
  return { ...target, session };
109
110
  }
110
111
  const active = store.getActiveSessionSync(target.session.channel, target.session.channelId);
111
- if (!active || active.deletedAt || active.id !== target.session.id || active.threadId) {
112
+ if (!active || active.deletedAt || active.id !== target.session.id || isTopicSession(active)) {
112
113
  throw sessionLoopError('CONFLICT', '当前主会话已发生变化,请重新查询');
113
114
  }
114
115
  updateRelationSessionLoop(active, config);
@@ -430,6 +431,7 @@ export class SessionIdleMonitor {
430
431
  state.emitted = true;
431
432
  if (!this.persistState())
432
433
  return;
434
+ const topicThreadId = sessionTopicThreadId(session);
433
435
  this.options.eventBus.publish({
434
436
  type: 'session:idle',
435
437
  sessionId,
@@ -438,7 +440,7 @@ export class SessionIdleMonitor {
438
440
  channelName: session.channel,
439
441
  channelId: session.channelId,
440
442
  chatType: session.chatType,
441
- ...(session.threadId ? { threadId: session.threadId } : {}),
443
+ ...(topicThreadId ? { threadId: topicThreadId } : {}),
442
444
  ...(session.metadata?.peerId || session.channelId ? { peerId: session.metadata?.peerId || session.channelId } : {}),
443
445
  ...(typeof state.encrypted === 'boolean'
444
446
  ? { encrypted: state.encrypted }
@@ -491,12 +493,12 @@ export class SessionIdleMonitor {
491
493
  }
492
494
  isEligibleSession(session) {
493
495
  return !!resolveSessionLoopTarget(session)
494
- && (!!session.threadId || !this.options.isCurrentMainSession || this.options.isCurrentMainSession(session));
496
+ && (isTopicSession(session) || !this.options.isCurrentMainSession || this.options.isCurrentMainSession(session));
495
497
  }
496
498
  async disarmMainSession(sessionId) {
497
499
  try {
498
500
  const session = await this.options.getSessionById(sessionId);
499
- if (!session || session.threadId)
501
+ if (!session || isTopicSession(session))
500
502
  return;
501
503
  this.clearTimers(sessionId);
502
504
  this.generations.set(sessionId, (this.generations.get(sessionId) ?? 0) + 1);
@@ -509,7 +511,7 @@ export class SessionIdleMonitor {
509
511
  try {
510
512
  const session = await this.options.getSessionById(sessionId);
511
513
  const state = this.sessions.get(sessionId);
512
- if (!session || session.threadId || !state || !this.isEligibleSession(session))
514
+ if (!session || isTopicSession(session) || !state || !this.isEligibleSession(session))
513
515
  return;
514
516
  this.clearTimers(sessionId);
515
517
  this.generations.set(sessionId, (this.generations.get(sessionId) ?? 0) + 1);
@@ -525,7 +527,7 @@ export class SessionIdleMonitor {
525
527
  async pruneInactiveMainSession(sessionId) {
526
528
  try {
527
529
  const session = await this.options.getSessionById(sessionId);
528
- if (session && !session.threadId && !this.isEligibleSession(session))
530
+ if (session && isMainSession(session) && !this.isEligibleSession(session))
529
531
  await this.disarmMainSession(sessionId);
530
532
  }
531
533
  catch (error) {