evolcore 0.0.32 → 0.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/bin/install-codex-managed-hooks.mjs +124 -37
- package/dist/agents/claude-runner.js +29 -2
- package/dist/agents/codex/app-server-client.js +3 -0
- package/dist/agents/codex/diagnostics.js +76 -44
- package/dist/agents/codex-runner.js +23 -19
- package/dist/agents/ecagent-runner.js +5 -13
- package/dist/aun/group/operations.js +0 -1
- package/dist/aun/msg/history.js +1 -0
- package/dist/aun/msg/p2p.js +0 -1
- package/dist/aun/outbox.js +50 -1
- package/dist/channels/aun.js +28 -2
- package/dist/channels/daemon.js +257 -2
- package/dist/cli/agent-command.js +58 -24
- package/dist/cli/agent.js +1 -1
- package/dist/cli/aun-commands.js +365 -29
- package/dist/cli/contact.js +8 -2
- package/dist/cli/daemon-commands.js +81 -12
- package/dist/cli/debug-mode.js +154 -0
- package/dist/cli/handoff-command.js +3 -0
- package/dist/cli/index.js +81 -64
- package/dist/cli/init.js +2 -2
- package/dist/cli/managed-command-guard.js +5 -0
- package/dist/cli/memory-command.js +8 -1
- package/dist/cli/queue-command.js +9 -4
- package/dist/cli/session-command.js +194 -0
- package/dist/cli/skill-command.js +78 -2
- package/dist/cli/task-context.js +1 -0
- package/dist/cli/thread-command.js +620 -0
- package/dist/cli/trigger-command.js +4 -2
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-field-policy.js +2 -3
- package/dist/config/config-manager.js +7 -2
- package/dist/config/resolved-config-op.js +3 -0
- package/dist/config-store.js +15 -0
- package/dist/core/auth/agent-delegation.js +25 -1
- package/dist/core/auth/debug-context.js +139 -0
- package/dist/core/auth/operation-authorizer.js +24 -1
- package/dist/core/auth/operation-catalog.js +33 -0
- package/dist/core/background-session/session-cleanup.js +212 -0
- package/dist/core/background-session/types.js +10 -0
- package/dist/core/bootstrap-messages.js +1 -1
- package/dist/core/bootstrap-service.js +6 -3
- package/dist/core/command/command-handler.js +60 -24
- package/dist/core/command/memory-slash.js +6 -2
- package/dist/core/command/menu-catalog.js +48 -1
- package/dist/core/command/menu-handler.js +92 -16
- package/dist/core/command/menu-protocol.js +24 -1
- package/dist/core/command/skill-slash.js +322 -0
- package/dist/core/command/slash-gate.js +6 -4
- package/dist/core/command/slash-handler.js +193 -13
- package/dist/core/command/topic-slash.js +528 -0
- package/dist/core/evolagent-registry.js +20 -5
- package/dist/core/handoff/runtime.js +5 -11
- package/dist/core/handoff/store.js +5 -0
- package/dist/core/ipc-registration.js +139 -2
- package/dist/core/memory/consolidation-context.js +49 -16
- package/dist/core/memory/consolidation-ops-records.js +35 -0
- package/dist/core/memory/consolidation-queue-format.js +21 -0
- package/dist/core/memory/consolidation.js +174 -180
- package/dist/core/message/aun-ipc-sender.js +161 -6
- package/dist/core/message/im-renderer.js +149 -6
- package/dist/core/message/message-bridge.js +13 -3
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/peer-mode.js +6 -1
- package/dist/core/message/response-engine.js +349 -67
- package/dist/core/message/subtask-tool-projection.js +140 -0
- package/dist/core/model/catalog-service.js +17 -12
- package/dist/core/permission/ec-command-parser.js +126 -7
- package/dist/core/session/session-idle-monitor.js +249 -22
- package/dist/core/session/session-manager.js +54 -9
- package/dist/core/skill/command-parser.js +13 -0
- package/dist/core/skill/index-generator.js +4 -1
- package/dist/core/skill/startup.js +41 -0
- package/dist/core/skill/switch-resolver.js +55 -29
- package/dist/core/thread/command-parser.js +125 -0
- package/dist/core/thread/index.js +1 -0
- package/dist/core/thread/ipc-operation.js +95 -0
- package/dist/core/thread/thread-context-storage.js +131 -0
- package/dist/core/thread/thread-operation.js +258 -0
- package/dist/core/thread/thread-route.js +33 -0
- package/dist/core/thread/thread-service.js +777 -0
- package/dist/core/thread/types.js +1 -0
- package/dist/eck/message-renderer.js +35 -0
- package/dist/index.js +8 -5
- package/dist/ipc.js +89 -10
- package/dist/paths.js +23 -0
- package/dist/response-system/engines/v1/proactive-flow.js +169 -80
- package/dist/trigger/authorization.js +57 -28
- package/dist/trigger/feedback.js +7 -1
- package/dist/trigger/parser.js +63 -1
- package/dist/trigger/validation.js +14 -0
- package/dist/utils/error-utils.js +22 -1
- package/kits/docs/context-assembly.md +1 -0
- package/kits/docs/evolcore/INDEX.md +1 -0
- package/kits/docs/evolcore/agent-create.md +39 -28
- package/kits/docs/evolcore/agent.md +19 -4
- package/kits/docs/evolcore/ec-capabilities.md +4 -0
- package/kits/docs/evolcore/group-collaboration.md +3 -5
- package/kits/docs/evolcore/group.md +3 -4
- package/kits/docs/evolcore/memory-consolidation.md +1 -1
- package/kits/docs/evolcore/menu-exec-schema-commands.md +2 -2
- package/kits/docs/evolcore/menu-protocol.md +7 -0
- package/kits/docs/evolcore/msg.md +5 -8
- package/kits/docs/evolcore/thread-chain.md +267 -0
- package/kits/docs/prompt-loading-architecture.md +1 -0
- package/kits/eck_manifest.json +61 -26
- package/kits/eck_message_manifest.json +169 -10
- package/kits/rules/05-venue.md +1 -1
- package/kits/rules/07-memory-and-thread.md +202 -0
- package/kits/schemas/relation-config.schema.9.json +18 -3
- package/kits/schemas/role-config.schema.1.json +6 -6
- package/kits/schemas/single-session.schema.1.json +6 -6
- package/kits/schemas/single-session.schema.2.json +6 -6
- package/kits/schemas/single-session.schema.3.json +6 -6
- package/kits/skills/.switches.json +12 -1
- package/kits/skills/ec-group/SKILL.md +234 -0
- package/kits/skills/ec-skill/SKILL.md +10 -1
- package/kits/skills/ec-skill/scripts/cli.mjs +15 -1
- package/kits/skills/scrapling-article-fetch/SKILL.md +128 -0
- package/kits/skills/scrapling-article-fetch/scripts/check_python_env.py +54 -0
- package/kits/skills/scrapling-article-fetch/scripts/render_markdown_reply.py +35 -0
- package/kits/skills/scrapling-article-fetch/scripts/run_pipeline.sh +16 -0
- package/kits/skills/scrapling-article-fetch/scripts/scrapling_fetch.py +344 -0
- package/kits/skills/thread-ops/SKILL.md +492 -0
- package/kits/skills/wechat-article-media-download/SKILL.md +65 -0
- package/kits/skills/wechat-article-media-download/scripts/download.py +163 -0
- package/kits/skills/wechat-article-reader/SKILL.md +97 -0
- package/kits/skills/wechat-article-reader/scripts/fetch.mjs +256 -0
- package/kits/templates/bootstrap-welcome.md +7 -7
- package/kits/templates/memory-consolidation/forget.md +5 -0
- package/kits/templates/memory-consolidation/generalize.md +5 -0
- package/kits/templates/memory-consolidation/profile.md +5 -0
- package/kits/templates/memory-consolidation/promote.md +5 -0
- package/kits/templates/message-fragments/item.md +2 -1
- package/kits/templates/message-fragments/thread-context.md +6 -0
- package/kits/templates/message-fragments/trigger-session.md +2 -0
- package/kits/templates/message-fragments/trigger-thread-check.md +2 -0
- package/kits/templates/roles/member.json +2 -0
- package/kits/templates/roles/visitor.json +2 -0
- package/kits/templates/system-fragments/bootstrap.md +66 -72
- package/kits/templates/system-fragments/channel.md +3 -6
- package/kits/templates/system-fragments/commands.md +1 -0
- package/kits/templates/system-fragments/managed-runtime.md +15 -0
- package/kits/templates/system-fragments/reply.md +3 -2
- package/kits/templates/system-fragments/session.md +5 -14
- package/package.json +3 -1
- package/kits/docs/evolcore/group-fs.md +0 -17
- package/kits/docs/venues/aun-group.md +0 -45
- package/kits/docs/venues/group.md +0 -43
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,30 @@
|
|
|
5
5
|
|
|
6
6
|
## Unreleased
|
|
7
7
|
|
|
8
|
+
## 0.0.33 (2026-10-15)
|
|
9
|
+
|
|
10
|
+
### 话题与会话
|
|
11
|
+
|
|
12
|
+
- 新增话题管理与跨话题协作,支持查询历史、维护摘要和状态、删除与恢复话题,并通过定时复查跟进跨话题消息。
|
|
13
|
+
- 新增当前主会话和话题的闲置触发设置,可通过聊天命令或菜单单独开启、关闭和调整等待时间。
|
|
14
|
+
- 支持按会话编号查找本地存档,完善话题上下文衔接和后台会话清理。
|
|
15
|
+
- 改进消息中断与任务恢复,避免已提交输入重复执行或未提交输入丢失。
|
|
16
|
+
- 优化后台任务与子智能体的状态展示,将关联任务结果合并到原工具卡片,减少重复提示,并正确展示耗时及完成、失败、停止等状态。
|
|
17
|
+
|
|
18
|
+
### Agent 与模型
|
|
19
|
+
|
|
20
|
+
- 简化首次设定:沿用已有有效资料,仅确认缺失、歧义或新增内容;人格和头像可稍后完善,正式欢迎文案由 Agent 自主准备。
|
|
21
|
+
- 完善模型推理强度选项,在模型目录未提供能力信息时按基座支持范围提供选项,并统一角色与关系设置。
|
|
22
|
+
- 强化 Codex 安装与启动检查,支持修复失效绑定,切换到另一套有效安装路径前要求明确确认。
|
|
23
|
+
- 改进 Codex 工具结果诊断,区分策略拒绝、用户拒绝和实际执行结果,减少错误归因。
|
|
24
|
+
|
|
25
|
+
### 权限与运行稳定性
|
|
26
|
+
|
|
27
|
+
- 强化受管命令、话题操作及个人和群文件访问的身份与目标校验,发送被拒时提供更明确的原因。
|
|
28
|
+
- 无人值守任务不再请求交互审批,任务恢复与跨会话交接沿用原有审批策略。
|
|
29
|
+
- 查看任务运行上下文不再消耗一次性授权,避免后续操作重复审批。
|
|
30
|
+
- 修复重启提示被恢复后的进程重复投递的问题,并在无法判断错误原因时保留原始诊断信息。
|
|
31
|
+
|
|
8
32
|
## 0.0.32 (2026-10-12)
|
|
9
33
|
|
|
10
34
|
- 统一受管命令的原始参数与审批哈希校验,按会话项目目录解析 Agent 初始化、人格和头像文件,强化授权后的文件访问边界。
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
6
|
-
import { randomUUID } from 'node:crypto';
|
|
6
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
7
7
|
import { execFileSync } from 'node:child_process';
|
|
8
8
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
9
9
|
import { isDeepStrictEqual } from 'node:util';
|
|
@@ -285,8 +285,55 @@ export function samePackageRoot(embedded, expected) {
|
|
|
285
285
|
return normalize(embedded) === normalize(expected) || resolved(embedded) === resolved(expected);
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
+
function normalizedPath(value) {
|
|
289
|
+
return path.normalize(String(value || '')).replace(/[\\/]+$/, '').toLowerCase();
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function parseManagedCommand(value, hookPath) {
|
|
293
|
+
if (typeof value !== 'string') return null;
|
|
294
|
+
const match = /^"([^"\r\n]+)"\s+"([^"\r\n]+)"\s+%EVOLCORE_INSTANCE_SOCKET%$/i.exec(value.trim());
|
|
295
|
+
if (!match) return null;
|
|
296
|
+
const nodePath = match[1];
|
|
297
|
+
const commandHookPath = match[2];
|
|
298
|
+
if (!path.isAbsolute(nodePath) || /[%!]/.test(nodePath + commandHookPath)) return null;
|
|
299
|
+
if (normalizedPath(commandHookPath) !== normalizedPath(hookPath)) return null;
|
|
300
|
+
return { nodePath };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function runtimeFileMayExist(file) {
|
|
304
|
+
try { return fs.statSync(file).isFile(); }
|
|
305
|
+
catch (error) { return !['ENOENT', 'ENOTDIR'].includes(error?.code); }
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function managedBinding(source, launcher, hookPath) {
|
|
309
|
+
const embeddedPackageRoot = launcherPackageRoot(launcher);
|
|
310
|
+
if (!embeddedPackageRoot || !path.isAbsolute(embeddedPackageRoot)
|
|
311
|
+
|| launcher.replaceAll('\r\n', '\n') !== managedHookLauncher(embeddedPackageRoot)) return null;
|
|
312
|
+
const retained = removeManagedBlock(source);
|
|
313
|
+
if (retained === source) return null;
|
|
314
|
+
const original = parseToml(source);
|
|
315
|
+
const retainedConfig = parseToml(retained);
|
|
316
|
+
const groups = original.hooks?.PreToolUse;
|
|
317
|
+
const retainedGroups = retainedConfig.hooks?.PreToolUse || [];
|
|
318
|
+
const owned = groups.filter(group => !retainedGroups.some(other => isDeepStrictEqual(group, other)));
|
|
319
|
+
if (owned.length !== 1) return null;
|
|
320
|
+
const group = owned[0];
|
|
321
|
+
if (group.matcher !== '*' || !Array.isArray(group.hooks) || group.hooks.length !== 1) return null;
|
|
322
|
+
const handler = group.hooks[0];
|
|
323
|
+
if (handler.type !== 'command' || handler.async === true) return null;
|
|
324
|
+
const command = parseManagedCommand(handler.command, hookPath);
|
|
325
|
+
const windowsCommand = parseManagedCommand(handler.command_windows, hookPath);
|
|
326
|
+
if (!command || !windowsCommand || normalizedPath(command.nodePath) !== normalizedPath(windowsCommand.nodePath)) return null;
|
|
327
|
+
if (!isDeepStrictEqual(group, { matcher: '*', hooks: [{
|
|
328
|
+
type: 'command', command: handler.command, command_windows: handler.command_windows,
|
|
329
|
+
statusMessage: 'Checking EvolCore session policy',
|
|
330
|
+
}] })) return null;
|
|
331
|
+
if (retainedGroups.some(other => JSON.stringify(other).toLowerCase().includes('pre-tool-use.mjs'))) return null;
|
|
332
|
+
return { embeddedPackageRoot, managedNodePath: command.nodePath };
|
|
333
|
+
}
|
|
334
|
+
|
|
288
335
|
/** Inspect only the canonical EvolCore managed-hook targets. */
|
|
289
|
-
export function inspectManagedHookInstallation({ requirements, managedDir, packageRoot }) {
|
|
336
|
+
export function inspectManagedHookInstallation({ requirements, managedDir, packageRoot, nodePath = process.execPath }) {
|
|
290
337
|
const canonicalRequirements = path.resolve(requirements);
|
|
291
338
|
const canonicalManagedDir = path.resolve(managedDir);
|
|
292
339
|
const hookPath = path.join(canonicalManagedDir, 'pre-tool-use.mjs');
|
|
@@ -297,11 +344,12 @@ export function inspectManagedHookInstallation({ requirements, managedDir, packa
|
|
|
297
344
|
return { status: 'unsafe', requirements: canonicalRequirements, managedDir: canonicalManagedDir, hookPath, reason: `${directory} must be a real directory` };
|
|
298
345
|
}
|
|
299
346
|
} catch (error) {
|
|
300
|
-
if (error?.code !== 'ENOENT') return { status: '
|
|
347
|
+
if (error?.code !== 'ENOENT') return { status: 'unavailable', requirements: canonicalRequirements, managedDir: canonicalManagedDir, hookPath, reason: error?.message || String(error) };
|
|
301
348
|
}
|
|
302
349
|
}
|
|
303
350
|
const req = regularFile(canonicalRequirements);
|
|
304
351
|
const hook = regularFile(hookPath);
|
|
352
|
+
if (req.error || hook.error) return { status: 'unavailable', requirements: canonicalRequirements, managedDir: canonicalManagedDir, hookPath, reason: 'unable to inspect requirements or launcher' };
|
|
305
353
|
if (req.unsafe || hook.unsafe) return { status: 'unsafe', requirements: canonicalRequirements, managedDir: canonicalManagedDir, hookPath, reason: 'requirements or launcher is not a regular file' };
|
|
306
354
|
if (!req.ok && hook.ok) return { status: 'unsafe', requirements: canonicalRequirements, managedDir: canonicalManagedDir, hookPath, reason: 'canonical launcher exists but requirements.toml is missing' };
|
|
307
355
|
if (req.ok && !hook.ok) {
|
|
@@ -324,48 +372,81 @@ export function inspectManagedHookInstallation({ requirements, managedDir, packa
|
|
|
324
372
|
}
|
|
325
373
|
}
|
|
326
374
|
if (!req.ok || !hook.ok) return { status: 'missing', requirements: canonicalRequirements, managedDir: canonicalManagedDir, hookPath, reason: 'requirements or launcher is missing' };
|
|
327
|
-
let
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
375
|
+
let source;
|
|
376
|
+
let launcher;
|
|
377
|
+
try {
|
|
378
|
+
source = fs.readFileSync(canonicalRequirements, 'utf8');
|
|
379
|
+
launcher = fs.readFileSync(hookPath, 'utf8');
|
|
380
|
+
} catch (error) {
|
|
381
|
+
return { status: 'unavailable', requirements: canonicalRequirements, managedDir: canonicalManagedDir, hookPath, reason: error?.message || String(error) };
|
|
382
|
+
}
|
|
383
|
+
try {
|
|
384
|
+
const parsed = parseToml(source);
|
|
385
|
+
const binding = managedBinding(source, launcher, hookPath);
|
|
386
|
+
if (!binding || parsed.allow_managed_hooks_only !== true || parsed.features?.hooks !== true
|
|
387
|
+
|| normalizedPath(parsed.hooks?.windows_managed_dir) !== normalizedPath(canonicalManagedDir)
|
|
388
|
+
|| (parsed.hooks?.managed_dir !== undefined
|
|
389
|
+
&& normalizedPath(parsed.hooks.managed_dir) !== normalizedPath(canonicalManagedDir))) {
|
|
390
|
+
throw new Error('managed-hook policy is missing, altered, or points outside the canonical target');
|
|
391
|
+
}
|
|
392
|
+
const { embeddedPackageRoot, managedNodePath } = binding;
|
|
393
|
+
const packageMatches = samePackageRoot(embeddedPackageRoot, path.resolve(packageRoot || process.cwd()));
|
|
394
|
+
const nodeMatches = samePackageRoot(managedNodePath, nodePath);
|
|
395
|
+
const state = {
|
|
396
|
+
requirements: canonicalRequirements, managedDir: canonicalManagedDir, hookPath,
|
|
397
|
+
managedPackageRoot: embeddedPackageRoot, managedNodePath,
|
|
398
|
+
repairFingerprint: createHash('sha256').update(source).update('\0').update(launcher).digest('hex'),
|
|
399
|
+
};
|
|
400
|
+
if (packageMatches && nodeMatches) return { ...state, status: 'installed' };
|
|
401
|
+
return {
|
|
402
|
+
...state, status: 'stale',
|
|
403
|
+
repairAmbiguous: runtimeFileMayExist(path.join(embeddedPackageRoot, 'bin', 'codex-managed-hook.mjs'))
|
|
404
|
+
&& runtimeFileMayExist(managedNodePath),
|
|
405
|
+
reason: !packageMatches && !nodeMatches
|
|
406
|
+
? 'managed-hook points to a different EvolCore package root and Node runtime'
|
|
407
|
+
: !packageMatches ? 'managed-hook points to a different EvolCore package root'
|
|
408
|
+
: 'managed-hook points to a different Node runtime',
|
|
409
|
+
};
|
|
410
|
+
} catch (error) {
|
|
411
|
+
return { status: 'unsafe', requirements: canonicalRequirements, managedDir: canonicalManagedDir, hookPath, reason: error?.message || String(error) };
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/** Idempotently repair a missing or stale canonical installation; unsafe edits fail closed. */
|
|
356
416
|
export function repairManagedHookInstallation(options, acl = windowsAcl) {
|
|
357
417
|
const { requirements, managedDir, packageRoot, nodePath = process.execPath } = options;
|
|
358
418
|
const canonicalRequirements = path.resolve(requirements);
|
|
359
419
|
const canonicalManagedDir = path.resolve(managedDir);
|
|
360
420
|
const hookPath = path.join(canonicalManagedDir, 'pre-tool-use.mjs');
|
|
361
421
|
const sourceHook = path.join(packageRoot, 'bin', 'codex-managed-hook.mjs');
|
|
362
|
-
const state = inspectManagedHookInstallation({ requirements: canonicalRequirements, managedDir: canonicalManagedDir, packageRoot });
|
|
422
|
+
const state = inspectManagedHookInstallation({ requirements: canonicalRequirements, managedDir: canonicalManagedDir, packageRoot, nodePath });
|
|
363
423
|
if (state.status === 'installed') return { ...state, repaired: false, warnings: [] };
|
|
364
|
-
if (state.status
|
|
424
|
+
if (!['missing', 'stale'].includes(state.status)) throw new Error(`Refusing automatic managed-hook repair: ${state.reason}`);
|
|
425
|
+
if (state.status === 'stale' && (options.expectedFingerprint !== state.repairFingerprint
|
|
426
|
+
|| (state.repairAmbiguous && options.confirmRebind !== true))) {
|
|
427
|
+
throw new Error('Refusing automatic managed-hook repair: binding changed or requires explicit confirmation');
|
|
428
|
+
}
|
|
365
429
|
if (!fs.existsSync(sourceHook)) throw new Error(`Required Hook runtime file was not found: ${sourceHook}`);
|
|
366
430
|
const previousContent = fs.existsSync(canonicalRequirements) ? fs.readFileSync(canonicalRequirements, 'utf8') : '';
|
|
367
431
|
const content = buildManagedRequirements(previousContent, { targetDirectory: canonicalManagedDir, nodePath, hookPath });
|
|
368
|
-
const
|
|
432
|
+
const inspect = () => inspectManagedHookInstallation({
|
|
433
|
+
requirements: canonicalRequirements, managedDir: canonicalManagedDir, packageRoot, nodePath,
|
|
434
|
+
});
|
|
435
|
+
const warnings = provisionManagedFiles({
|
|
436
|
+
requirements: canonicalRequirements, hookPath, sourceHook, nodePath, previousContent, content,
|
|
437
|
+
launcher: managedHookLauncher(packageRoot),
|
|
438
|
+
beforePublish: () => {
|
|
439
|
+
const latest = inspect();
|
|
440
|
+
if (latest.status !== state.status || latest.repairFingerprint !== state.repairFingerprint
|
|
441
|
+
|| latest.repairAmbiguous !== state.repairAmbiguous) {
|
|
442
|
+
throw new Error('Managed-hook state changed before publication; retry the startup check');
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
verifyPublished: () => {
|
|
446
|
+
const verified = inspect();
|
|
447
|
+
if (verified.status !== 'installed') throw new Error('Managed-hook verification failed: ' + verified.reason);
|
|
448
|
+
},
|
|
449
|
+
}, acl);
|
|
369
450
|
return { status: 'installed', requirements: canonicalRequirements, managedDir: canonicalManagedDir, hookPath, repaired: true, warnings };
|
|
370
451
|
}
|
|
371
452
|
|
|
@@ -475,6 +556,7 @@ export function provisionManagedFiles(options, acl = windowsAcl) {
|
|
|
475
556
|
let committed = false;
|
|
476
557
|
try {
|
|
477
558
|
fs.writeFileSync(lock, JSON.stringify({ pid: process.pid, started: new Date().toISOString() }));
|
|
559
|
+
options.beforePublish?.();
|
|
478
560
|
const existing = existingFile(requirements, fs);
|
|
479
561
|
// Detect edits made after configuration generation, including a second
|
|
480
562
|
// installer completing just before this process acquired the lock.
|
|
@@ -509,11 +591,13 @@ export function provisionManagedFiles(options, acl = windowsAcl) {
|
|
|
509
591
|
const target = temporary ? entry.temporary : entry.file;
|
|
510
592
|
if (fs.readFileSync(target, 'utf8') !== entry.value) throw new Error(`Installed content differs: ${target}`);
|
|
511
593
|
}
|
|
512
|
-
|
|
594
|
+
const report = acl('verify', { entries: [
|
|
513
595
|
{ path: directory, protected: true, execute: true },
|
|
514
596
|
...staged.map(entry => ({ path: temporary ? entry.temporary : entry.file, protected: true })),
|
|
515
597
|
...readable,
|
|
516
598
|
] });
|
|
599
|
+
if (temporary === false) options.verifyPublished?.();
|
|
600
|
+
return report;
|
|
517
601
|
};
|
|
518
602
|
verify(true);
|
|
519
603
|
let accessReport;
|
|
@@ -561,7 +645,10 @@ function main() {
|
|
|
561
645
|
if (!fs.statSync(file).isFile()) fail(`Required Hook runtime file was not found: ${file}`);
|
|
562
646
|
}
|
|
563
647
|
if (hasArgument('--repair')) {
|
|
564
|
-
const result = repairManagedHookInstallation({
|
|
648
|
+
const result = repairManagedHookInstallation({
|
|
649
|
+
requirements, managedDir: targetDirectory, packageRoot, nodePath,
|
|
650
|
+
expectedFingerprint: argument('--expected-fingerprint'), confirmRebind: hasArgument('--confirm-rebind'),
|
|
651
|
+
});
|
|
565
652
|
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
566
653
|
return;
|
|
567
654
|
}
|
|
@@ -586,6 +586,8 @@ export class AgentRunner {
|
|
|
586
586
|
permissionGateway;
|
|
587
587
|
sendPromptFn;
|
|
588
588
|
permissionContexts = new Map();
|
|
589
|
+
/** tool_use_id → 模型返回序号(turnCount),供 PreToolUse 钩子把 turn 传给 policyHook */
|
|
590
|
+
toolTurnByCallId = new Map();
|
|
589
591
|
claudeExecutablePath;
|
|
590
592
|
/** 每个 session 最近的子进程 stderr 行(环形缓冲),用于子进程崩溃时还原真正原因 */
|
|
591
593
|
recentStderr = new Map();
|
|
@@ -714,6 +716,9 @@ export class AgentRunner {
|
|
|
714
716
|
ANTHROPIC_API_KEY: this.apiKey,
|
|
715
717
|
PATH: process.env.PATH,
|
|
716
718
|
DISABLE_AUTOUPDATER: '1',
|
|
719
|
+
// 记忆只走 EvolCore(ec memory):关闭 Claude Code 原生 auto-memory 的
|
|
720
|
+
// 读写,避免两套记忆并存污染上下文。
|
|
721
|
+
CLAUDE_CODE_DISABLE_AUTO_MEMORY: '1',
|
|
717
722
|
...(anthropicBaseUrl ? { ANTHROPIC_BASE_URL: anthropicBaseUrl } : {}),
|
|
718
723
|
...(this.customHeaders
|
|
719
724
|
? { ANTHROPIC_CUSTOM_HEADERS: Object.entries(this.customHeaders).map(([name, value]) => `${name}: ${value}`).join('\n') }
|
|
@@ -779,6 +784,9 @@ export class AgentRunner {
|
|
|
779
784
|
settings: {
|
|
780
785
|
...settings,
|
|
781
786
|
skillOverrides: { ...skillOverrides, eclink: 'off' },
|
|
787
|
+
// 记忆只走 EvolCore(ec memory):关闭 Claude Code 原生 auto-memory,
|
|
788
|
+
// 与 getAgentEnv 的 CLAUDE_CODE_DISABLE_AUTO_MEMORY 双保险。
|
|
789
|
+
autoMemoryEnabled: false,
|
|
782
790
|
},
|
|
783
791
|
};
|
|
784
792
|
}
|
|
@@ -1765,6 +1773,8 @@ export class AgentRunner {
|
|
|
1765
1773
|
if (content.type === 'tool_use') {
|
|
1766
1774
|
if (content.id)
|
|
1767
1775
|
toolUseNames.set(content.id, content.name);
|
|
1776
|
+
if (content.id)
|
|
1777
|
+
this.toolTurnByCallId.set(content.id, turnCount);
|
|
1768
1778
|
if (content.id && content.name === 'Bash' && content.input?.dangerouslyDisableSandbox !== true
|
|
1769
1779
|
&& resolvePhaseOneExecutionSandbox(permissionMode, this.permissionContexts.get(sessionId)?.role).state !== 'off') {
|
|
1770
1780
|
sandboxedBashCalls.add(content.id);
|
|
@@ -1772,7 +1782,9 @@ export class AgentRunner {
|
|
|
1772
1782
|
yield { type: 'tool_use', name: content.name, input: content.input, callId: content.id, turn: turnCount, outputTokens: turnOutputChars, assistantUuid: event.uuid };
|
|
1773
1783
|
}
|
|
1774
1784
|
else if (content.type === 'text' && content.text) {
|
|
1775
|
-
|
|
1785
|
+
// 携带出身标记:`<synthetic>` assistant 是 API/传输故障的错误回显,
|
|
1786
|
+
// 消费端据此把它与模型正文区分开,不必靠字符串或计数猜。
|
|
1787
|
+
yield { type: 'text', text: content.text, outputTokens: turnOutputChars, turn: turnCount, assistantUuid: event.uuid, synthetic: isSyntheticAssistant || undefined };
|
|
1776
1788
|
}
|
|
1777
1789
|
}
|
|
1778
1790
|
}
|
|
@@ -1787,6 +1799,8 @@ export class AgentRunner {
|
|
|
1787
1799
|
: block.content != null ? JSON.stringify(block.content) : '';
|
|
1788
1800
|
if (block.tool_use_id)
|
|
1789
1801
|
emittedToolResultIds.add(block.tool_use_id);
|
|
1802
|
+
if (block.tool_use_id)
|
|
1803
|
+
this.toolTurnByCallId.delete(block.tool_use_id);
|
|
1790
1804
|
if (sandboxedBashCalls.delete(block.tool_use_id) && block.is_error === true
|
|
1791
1805
|
&& isSandboxInitializationToolResult(block.content)) {
|
|
1792
1806
|
const { detail } = await this.recordSandboxInfrastructureFailure(sessionId, block.tool_use_id, permissionMode, new Error(resultContent), [], 'session');
|
|
@@ -2359,7 +2373,20 @@ export class AgentRunner {
|
|
|
2359
2373
|
});
|
|
2360
2374
|
}
|
|
2361
2375
|
// proactive 模式行为策略(首次工具调用必须是 ec msg send)
|
|
2362
|
-
const
|
|
2376
|
+
const sessionPolicyHook = this.permissionContexts.get(sessionId)?.policyHook;
|
|
2377
|
+
let policyResult;
|
|
2378
|
+
if (sessionPolicyHook) {
|
|
2379
|
+
// 钩子可能先于流里的 tool_use 事件到达:按 tool_use_id 取模型返回序号,取不到短等(最多 200ms)
|
|
2380
|
+
const policyCallId = (input.tool_use_id ?? input.toolUseID);
|
|
2381
|
+
let policyTurn = policyCallId ? this.toolTurnByCallId.get(policyCallId) : undefined;
|
|
2382
|
+
for (let waited = 0; policyCallId && policyTurn === undefined && waited < 200; waited += 20) {
|
|
2383
|
+
await new Promise(resolve => setTimeout(resolve, 20));
|
|
2384
|
+
policyTurn = this.toolTurnByCallId.get(policyCallId);
|
|
2385
|
+
}
|
|
2386
|
+
if (policyCallId)
|
|
2387
|
+
this.toolTurnByCallId.delete(policyCallId);
|
|
2388
|
+
policyResult = sessionPolicyHook(toolName, toolInput, { turn: policyTurn });
|
|
2389
|
+
}
|
|
2363
2390
|
if (policyResult?.block) {
|
|
2364
2391
|
await recordPreflightDenial(toolName, toolInput, undefined, input.tool_use_id ?? input.toolUseID, policyResult.policyCode ?? 'session_policy_hook');
|
|
2365
2392
|
return hookDecision('deny', policyResult.reason || '当前会话策略拒绝此工具调用', updatedInput);
|
|
@@ -745,6 +745,9 @@ export class CodexAppServerClient {
|
|
|
745
745
|
}
|
|
746
746
|
buildProcessArgs() {
|
|
747
747
|
const args = ['app-server', '--listen', 'stdio://'];
|
|
748
|
+
// 记忆只走 EvolCore(ec memory):显式关闭 Codex 原生 memories,
|
|
749
|
+
// 不依赖其默认值(当前 stable 默认 false,此处写死防未来翻转)。
|
|
750
|
+
args.push('--config', 'features.memories=false');
|
|
748
751
|
if (this.options.enableRequestUserInput !== false) {
|
|
749
752
|
args.push('--enable', 'default_mode_request_user_input');
|
|
750
753
|
}
|
|
@@ -2,7 +2,9 @@ import fs from 'node:fs';
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { spawnSync } from 'node:child_process';
|
|
5
|
+
import { createHash } from 'node:crypto';
|
|
5
6
|
import { parse as parseToml } from 'smol-toml';
|
|
7
|
+
const managedHookInstaller = await import(new URL('../../../bin/install-codex-managed-hooks.mjs', import.meta.url).href);
|
|
6
8
|
export function checkCodexProjectPath(value, options = {}) {
|
|
7
9
|
const configured = typeof value === 'string' ? value.trim() : '';
|
|
8
10
|
if (!configured) {
|
|
@@ -180,19 +182,6 @@ function realDirectoryState(directory) {
|
|
|
180
182
|
return 'unavailable';
|
|
181
183
|
}
|
|
182
184
|
}
|
|
183
|
-
/** Read back the package root that managedHookLauncher() embedded. */
|
|
184
|
-
function launcherPackageRoot(launcher) {
|
|
185
|
-
const match = /^\s*const packageRoot = ("(?:[^"\\]|\\.)*")\s*;/m.exec(launcher);
|
|
186
|
-
if (!match)
|
|
187
|
-
return null;
|
|
188
|
-
try {
|
|
189
|
-
const value = JSON.parse(match[1]);
|
|
190
|
-
return typeof value === 'string' && value ? value : null;
|
|
191
|
-
}
|
|
192
|
-
catch {
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
185
|
/**
|
|
197
186
|
* A `npm link` install reaches the package through a symlinked global path, so
|
|
198
187
|
* the launcher and the running CLI legitimately spell the same package root
|
|
@@ -213,6 +202,20 @@ function samePackageRoot(embedded, expected) {
|
|
|
213
202
|
};
|
|
214
203
|
return normalize(embedded) === normalize(expected) || resolved(embedded) === resolved(expected);
|
|
215
204
|
}
|
|
205
|
+
function normalizedPath(value) {
|
|
206
|
+
return path.normalize(String(value || '')).replace(/[\\/]+$/, '').toLowerCase();
|
|
207
|
+
}
|
|
208
|
+
function runtimeFileMayExist(file) {
|
|
209
|
+
try {
|
|
210
|
+
return fs.statSync(file).isFile();
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
return !['ENOENT', 'ENOTDIR'].includes(error?.code || '');
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function managedBinding(source, launcher, hookPath) {
|
|
217
|
+
return managedHookInstaller.managedBinding(source, launcher, hookPath);
|
|
218
|
+
}
|
|
216
219
|
/** Read-only inspection of the machine-wide Windows Codex managed hook. */
|
|
217
220
|
export function inspectCodexManagedHook(options = {}) {
|
|
218
221
|
const paths = codexManagedHookPaths(options);
|
|
@@ -283,32 +286,35 @@ export function inspectCodexManagedHook(options = {}) {
|
|
|
283
286
|
catch (error) {
|
|
284
287
|
return { status: 'unavailable', requirements, managedDir, hookPath, reason: error instanceof Error ? error.message : String(error) };
|
|
285
288
|
}
|
|
286
|
-
const normalized = (value) => path.normalize(String(value || '')).toLowerCase();
|
|
287
289
|
const expectedPackageRoot = path.resolve(options.packageRoot || process.cwd());
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
return
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
290
|
+
try {
|
|
291
|
+
const binding = managedBinding(source, launcher, hookPath);
|
|
292
|
+
if (!binding || config.allow_managed_hooks_only !== true || config.features?.hooks !== true
|
|
293
|
+
|| normalizedPath(config.hooks?.windows_managed_dir) !== normalizedPath(managedDir)
|
|
294
|
+
|| (config.hooks?.managed_dir !== undefined
|
|
295
|
+
&& normalizedPath(config.hooks.managed_dir) !== normalizedPath(managedDir))) {
|
|
296
|
+
throw new Error('managed-hook policy is missing, altered, or points outside the canonical target');
|
|
297
|
+
}
|
|
298
|
+
const { embeddedPackageRoot, managedNodePath } = binding;
|
|
299
|
+
const packageMatches = samePackageRoot(embeddedPackageRoot, expectedPackageRoot);
|
|
300
|
+
const nodeMatches = samePackageRoot(managedNodePath, options.nodePath || process.execPath);
|
|
301
|
+
const repairFingerprint = createHash('sha256').update(source).update('\0').update(launcher).digest('hex');
|
|
302
|
+
if (packageMatches && nodeMatches)
|
|
303
|
+
return { status: 'installed', requirements, managedDir, hookPath };
|
|
304
|
+
return {
|
|
305
|
+
status: 'stale', requirements, managedDir, hookPath,
|
|
306
|
+
managedPackageRoot: embeddedPackageRoot, managedNodePath, repairFingerprint,
|
|
307
|
+
repairAmbiguous: runtimeFileMayExist(path.join(embeddedPackageRoot, 'bin', 'codex-managed-hook.mjs'))
|
|
308
|
+
&& runtimeFileMayExist(managedNodePath),
|
|
309
|
+
reason: !packageMatches && !nodeMatches
|
|
310
|
+
? 'managed-hook points to a different EvolCore package root and Node runtime'
|
|
311
|
+
: !packageMatches ? 'managed-hook points to a different EvolCore package root'
|
|
312
|
+
: 'managed-hook points to a different Node runtime',
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
return { status: 'unsafe', requirements, managedDir, hookPath, reason: error instanceof Error ? error.message : String(error) };
|
|
317
|
+
}
|
|
312
318
|
}
|
|
313
319
|
function powershellLiteral(value) {
|
|
314
320
|
return `'${value.replaceAll("'", "''")}'`;
|
|
@@ -319,7 +325,10 @@ function windowsArgument(value) {
|
|
|
319
325
|
}
|
|
320
326
|
export function buildCodexManagedHookElevationCommand(options) {
|
|
321
327
|
const argumentLine = [options.installer, '--package-root', options.packageRoot,
|
|
322
|
-
'--requirements', options.requirements, '--managed-dir', options.managedDir,
|
|
328
|
+
'--requirements', options.requirements, '--managed-dir', options.managedDir,
|
|
329
|
+
'--node-path', options.nodePath, '--repair',
|
|
330
|
+
...(options.expectedFingerprint ? ['--expected-fingerprint', options.expectedFingerprint] : []),
|
|
331
|
+
...(options.confirmRebind ? ['--confirm-rebind'] : [])]
|
|
323
332
|
.map(windowsArgument).join(' ');
|
|
324
333
|
return [
|
|
325
334
|
"$ErrorActionPreference = 'Stop'",
|
|
@@ -334,11 +343,11 @@ export function buildCodexManagedHookElevationCommand(options) {
|
|
|
334
343
|
].join('\n');
|
|
335
344
|
}
|
|
336
345
|
/**
|
|
337
|
-
* Ensure the machine-wide Windows hook before starting the daemon. Missing
|
|
338
|
-
* files are repaired through one UAC elevation; altered policy remains
|
|
346
|
+
* Ensure the machine-wide Windows hook before starting the daemon. Missing or
|
|
347
|
+
* stale files are repaired through one UAC elevation; altered policy remains
|
|
339
348
|
* fail-closed and is never overwritten automatically.
|
|
340
349
|
*/
|
|
341
|
-
export function ensureCodexManagedHook(options) {
|
|
350
|
+
export async function ensureCodexManagedHook(options) {
|
|
342
351
|
const platformName = options.platform ?? process.platform;
|
|
343
352
|
const paths = codexManagedHookPaths({
|
|
344
353
|
platform: platformName,
|
|
@@ -358,8 +367,27 @@ export function ensureCodexManagedHook(options) {
|
|
|
358
367
|
let state = inspectCodexManagedHook(inspectOptions);
|
|
359
368
|
if (state.status === 'installed')
|
|
360
369
|
return { ...state, repaired: false };
|
|
361
|
-
if (state.status !== 'missing')
|
|
370
|
+
if (state.status !== 'missing' && state.status !== 'stale')
|
|
362
371
|
return { ...state, repaired: false };
|
|
372
|
+
let confirmedRebind = false;
|
|
373
|
+
if (state.status === 'stale' && state.repairAmbiguous) {
|
|
374
|
+
try {
|
|
375
|
+
confirmedRebind = await options.repairDecision?.(state) === true;
|
|
376
|
+
}
|
|
377
|
+
catch (error) {
|
|
378
|
+
return { ...state, repaired: false, error: error instanceof Error ? error.message : String(error) };
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if (state.status === 'stale' && state.repairAmbiguous && !confirmedRebind) {
|
|
382
|
+
return { ...state, repaired: false, cancelled: true, error: 'managed-hook repair cancelled or requires confirmation' };
|
|
383
|
+
}
|
|
384
|
+
const rechecked = inspectCodexManagedHook(inspectOptions);
|
|
385
|
+
if (rechecked.status === 'installed')
|
|
386
|
+
return { ...rechecked, repaired: false };
|
|
387
|
+
if (rechecked.status !== state.status || rechecked.repairFingerprint !== state.repairFingerprint
|
|
388
|
+
|| rechecked.repairAmbiguous !== state.repairAmbiguous) {
|
|
389
|
+
return { ...rechecked, repaired: false, error: 'managed-hook state changed during the startup check; retry to confirm the current binding' };
|
|
390
|
+
}
|
|
363
391
|
const installer = path.join(path.resolve(options.packageRoot), 'bin', 'install-codex-managed-hooks.mjs');
|
|
364
392
|
if (!fs.existsSync(installer)) {
|
|
365
393
|
return { ...state, repaired: false, error: `managed-hook installer is missing: ${installer}` };
|
|
@@ -370,6 +398,10 @@ export function ensureCodexManagedHook(options) {
|
|
|
370
398
|
nodePath: process.execPath, installer: script, packageRoot,
|
|
371
399
|
requirements: path.resolve(inspectOptions.requirements),
|
|
372
400
|
managedDir: path.resolve(inspectOptions.managedDir),
|
|
401
|
+
...(state.status === 'stale' ? {
|
|
402
|
+
expectedFingerprint: state.repairFingerprint,
|
|
403
|
+
confirmRebind: confirmedRebind,
|
|
404
|
+
} : {}),
|
|
373
405
|
});
|
|
374
406
|
const result = spawnSync(powershell, [
|
|
375
407
|
// Do not use -NonInteractive here: Start-Process -Verb RunAs must be
|
|
@@ -385,7 +417,7 @@ export function ensureCodexManagedHook(options) {
|
|
|
385
417
|
}
|
|
386
418
|
return { ok: true };
|
|
387
419
|
});
|
|
388
|
-
options.onInstallRequired?.();
|
|
420
|
+
options.onInstallRequired?.(state);
|
|
389
421
|
let elevated;
|
|
390
422
|
try {
|
|
391
423
|
elevated = runElevated(installer, path.resolve(options.packageRoot));
|
|
@@ -177,10 +177,10 @@ function parseManagedPreToolUseDenial(value) {
|
|
|
177
177
|
reason = 'EvolCore PreToolUse policy denied this tool call';
|
|
178
178
|
const policyCode = /当前汇报命令(?:未通过规范解析|不符合当前会话)/.test(reason)
|
|
179
179
|
? 'proactive_send_invalid'
|
|
180
|
-
:
|
|
181
|
-
? '
|
|
182
|
-
:
|
|
183
|
-
? '
|
|
180
|
+
: /连续违规\s*\d+\s*次,任务已中断/.test(reason)
|
|
181
|
+
? 'proactive_circuit_break'
|
|
182
|
+
: /未包含意图表达,本轮只允许\s+ec\s+(?:group\s+send|msg\s+send)/.test(reason)
|
|
183
|
+
? 'proactive_intent_required'
|
|
184
184
|
: 'managed_pre_tool_use_denied';
|
|
185
185
|
return { policyCode, reason };
|
|
186
186
|
}
|
|
@@ -2206,12 +2206,21 @@ export class CodexRunner {
|
|
|
2206
2206
|
const pending = [...this.unconfirmedInterrupts.entries()].filter(([, record]) => record.sessionId === sessionId || (threadId && record.threadId === threadId));
|
|
2207
2207
|
await Promise.all(pending.map(async ([key, record]) => {
|
|
2208
2208
|
try {
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2209
|
+
const response = await withInterruptDeadline(record.client.threadRead(record.threadId, false, 2_000), 'Codex interrupt verification', 2_000);
|
|
2210
|
+
const threadStatus = response.thread?.status?.type;
|
|
2211
|
+
if (threadStatus === 'idle' || threadStatus === 'notLoaded') {
|
|
2212
|
+
if (this.unconfirmedInterrupts.get(key) === record) {
|
|
2213
|
+
this.unconfirmedInterrupts.delete(key);
|
|
2214
|
+
record.unsubscribe?.();
|
|
2215
|
+
}
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
2218
|
+
if (threadStatus !== undefined)
|
|
2219
|
+
return;
|
|
2220
|
+
const history = await withInterruptDeadline(record.client.threadRead(record.threadId, true, 2_000), 'Codex interrupt history verification', 2_000);
|
|
2221
|
+
const turn = history.thread?.turns?.find(turn => turn.id === record.turnId);
|
|
2213
2222
|
if (turn && ['completed', 'failed', 'interrupted'].includes(String(turn.status))
|
|
2214
|
-
&& !
|
|
2223
|
+
&& !history.thread?.turns?.some(turn => turn.status === 'inProgress')
|
|
2215
2224
|
&& this.unconfirmedInterrupts.get(key) === record) {
|
|
2216
2225
|
this.unconfirmedInterrupts.delete(key);
|
|
2217
2226
|
record.unsubscribe?.();
|
|
@@ -4965,17 +4974,12 @@ export class CodexRunner {
|
|
|
4965
4974
|
}
|
|
4966
4975
|
: undefined;
|
|
4967
4976
|
const itemErrorText = outputTextForManagedHook(typeof item.error === 'string' ? item.error : item.error?.message);
|
|
4968
|
-
const
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
const managedHookDenial = (item.status === 'failed' || item.status === 'declined' || hookErrorText)
|
|
4972
|
-
? parseManagedPreToolUseDenial([
|
|
4973
|
-
item.aggregatedOutput,
|
|
4974
|
-
item.output,
|
|
4975
|
-
item.result,
|
|
4976
|
-
hookErrorText,
|
|
4977
|
-
].filter(value => value !== undefined && value !== null).map(outputTextForManagedHook).join('\n'))
|
|
4977
|
+
const hookOutput = item.type === 'commandExecution' && item.exitCode == null && !itemErrorText
|
|
4978
|
+
&& (item.status === 'failed' || item.status === 'declined')
|
|
4979
|
+
? [item.aggregatedOutput, item.output, item.result].map(outputTextForManagedHook).find(text => /^\s*(?:Script failed\s+)?(?:Script error:\s*)?(?:Command|Tool call) blocked by PreToolUse hook\s*:/i.test(text))
|
|
4978
4980
|
: undefined;
|
|
4981
|
+
const managedHookDenial = parseManagedPreToolUseDenial(itemErrorText)
|
|
4982
|
+
?? parseManagedPreToolUseDenial(hookOutput);
|
|
4979
4983
|
const managedHookFields = !denial && managedHookDenial
|
|
4980
4984
|
? {
|
|
4981
4985
|
isError: true,
|