evolcore 0.0.15 → 0.0.17
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 +31 -0
- package/README.md +1 -0
- package/dist/agents/claude-runner.js +148 -29
- package/dist/agents/codex-runner.js +36 -10
- package/dist/agents/ecagent-runner.js +4 -1
- package/dist/aun/msg/group.js +3 -1
- package/dist/aun/msg/p2p.js +8 -6
- package/dist/channels/aun.js +73 -20
- package/dist/channels/daemon.js +15 -9
- package/dist/cli/init.js +7 -3
- package/dist/cli/task-context.js +48 -3
- package/dist/config/config-manager.js +1 -0
- package/dist/core/audit/log-integrity.js +149 -0
- package/dist/core/auth/authorization-audit.js +73 -3
- package/dist/core/event-catalog.js +1 -0
- package/dist/core/message/response-engine.js +36 -21
- package/dist/core/permission/tool-error-code.js +47 -0
- package/dist/core/permission/tool-policy.js +61 -2
- package/dist/index.js +8 -4
- package/dist/ipc.js +11 -6
- package/dist/paths.js +18 -1
- package/dist/trigger/scheduler.js +5 -4
- package/dist/utils/cross-platform.js +1 -24
- package/dist/utils/error-utils.js +38 -0
- package/dist/utils/instance-registry.js +35 -27
- package/dist/utils/logger.js +41 -10
- package/dist/utils/windows-autostart.js +50 -9
- package/dist/utils/windows-output.js +36 -0
- package/kits/docs/evolcore/config.md +1 -0
- package/kits/schemas/agent-config.schema.10.json +6 -0
- package/kits/schemas/daemon.schema.1.json +1 -1
- package/kits/schemas/daemon.schema.2.json +1 -1
- package/kits/schemas/daemon.schema.3.json +1 -1
- package/kits/schemas/daemon.schema.4.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,37 @@
|
|
|
3
3
|
本文件记录 EvolCore 的重要变更。EvolClaw 版本线的历史记录已归档至
|
|
4
4
|
[`docs/_archive/CHANGELOG-evolclaw.md`](docs/_archive/CHANGELOG-evolclaw.md)。
|
|
5
5
|
|
|
6
|
+
## 0.0.17 (2026-08-25)
|
|
7
|
+
|
|
8
|
+
### Claude 与任务边界
|
|
9
|
+
|
|
10
|
+
- 统一识别 Claude 启动参数过长错误,记录结构化能力不可用审计并以明确终态结束任务,避免无效重试和敏感启动参数泄露。
|
|
11
|
+
|
|
12
|
+
### Codex 与 AUN 消息兼容
|
|
13
|
+
|
|
14
|
+
- 将运行时 BaseAgent 信息传入任务上下文,仅对 Codex AUN 私聊/群聊文本载荷解码一层 `\\n`/`\\r`/`\\t` 转义,保持其它 Runner 和原始换行不变。
|
|
15
|
+
|
|
16
|
+
### 临时目录与跨平台稳定性
|
|
17
|
+
|
|
18
|
+
- 改进受管临时目录创建根路径解析,在拒绝不可信 TMPDIR 时使用固定系统回退并兼容 macOS `/var` 实路径,强化跨平台私有目录校验。
|
|
19
|
+
|
|
20
|
+
## 0.0.16 (2026-08-25)
|
|
21
|
+
|
|
22
|
+
### 任务与消息可靠性
|
|
23
|
+
|
|
24
|
+
- 统一任务总执行时限与 AUN 附件投递重试边界,补充可恢复失败的退避与错误上下文,避免无配置时误设总超时。
|
|
25
|
+
- 优化会话响应、事件总线和守护进程生命周期,完善任务结束、重试和清理时的状态收敛。
|
|
26
|
+
|
|
27
|
+
### Windows 与运行兼容性
|
|
28
|
+
|
|
29
|
+
- 强化 Windows 进程识别、登录自启状态探测和命令输出解码,避免误识别其它 Node 服务并改善失败诊断。
|
|
30
|
+
- 修正 Windows Claude 沙箱 settings 的受管临时文件生命周期,收紧运行目录校验,并为过长的 Unix socket 路径提供隔离的短路径回退。
|
|
31
|
+
|
|
32
|
+
### 审计与权限边界
|
|
33
|
+
|
|
34
|
+
- 增强结构化日志完整性检查、生命周期关联和重复记录识别,统一工具错误分类与审计上下文。
|
|
35
|
+
- 收紧只读诊断、受保护路径和运行时锁访问策略,仅允许显式授权的有界源码诊断。
|
|
36
|
+
|
|
6
37
|
## 0.0.15 (2026-08-25)
|
|
7
38
|
|
|
8
39
|
### Codex 文件授权
|
package/README.md
CHANGED
|
@@ -162,6 +162,7 @@ ec init aun
|
|
|
162
162
|
"idleMonitor": {
|
|
163
163
|
"enabled": true, // 任务超时监控开关
|
|
164
164
|
"timeout": 120, // 超时阈值(秒),默认 120 秒
|
|
165
|
+
"maxExecutionTime": 7200, // 可选:任务总执行时限(秒);未配置、非数字或 <=0 时不限制
|
|
165
166
|
"safeModeThreshold": 3 // 连续超时 N 次后进入安全模式(设为 0 禁用安全模式)
|
|
166
167
|
},
|
|
167
168
|
"flushDelay": 4 // 工具活动消息聚合发送间隔(秒),默认 4 秒
|
|
@@ -11,6 +11,7 @@ import fs from 'fs';
|
|
|
11
11
|
import os from 'os';
|
|
12
12
|
import crypto from 'crypto';
|
|
13
13
|
import { logger } from '../utils/logger.js';
|
|
14
|
+
import { createClaudeStartupArgumentsTooLongError, isClaudeStartupArgumentsTooLong, } from '../utils/error-utils.js';
|
|
14
15
|
import { requestDangerousCommandPermission } from '../core/permission/approval-gateway.js';
|
|
15
16
|
import { checkDangerousCommand, checkReadonly, evaluateToolPreflight } from '../core/permission/tool-policy.js';
|
|
16
17
|
import { sanitizeShellExecutionEnvironment } from '../core/permission/shell-environment.js';
|
|
@@ -25,6 +26,7 @@ import { buildClaudeProtectedFilesystem, containsHClassReference, containsLClass
|
|
|
25
26
|
import { buildHClassGuardCommand, createSandboxInitializationError, isSandboxInitializationFailure, SANDBOX_INITIALIZATION_FAILED, ensureClaudeGitWorktreeConfig, prependExecutableDirectory, resolveBubblewrapPath, shouldFailIfClaudeSandboxUnavailable, } from '../core/permission/sandbox-runtime.js';
|
|
26
27
|
import { buildClaudeUnixSocketAllowlist } from '../core/permission/unix-socket-policy.js';
|
|
27
28
|
import { auditToolPreflightDenial } from '../core/auth/authorization-audit.js';
|
|
29
|
+
import { isManagedSessionRuntimeDir } from '../cli/task-context.js';
|
|
28
30
|
import { contextTokensForUsage, usageForContext, isClaudeContextUsageModel, isOneMillionContextModel, realContextWindowForModel, autoCompactWindowForModel } from './runner-types.js';
|
|
29
31
|
export { hasCompact, hasModelSwitcher, hasPermissionController } from './runner-types.js';
|
|
30
32
|
// Built-in tools execute inside the Claude runtime and are covered by the
|
|
@@ -243,6 +245,17 @@ function trustedRuntimeWritePaths(runtimeEnv) {
|
|
|
243
245
|
}
|
|
244
246
|
return [...new Set(paths)];
|
|
245
247
|
}
|
|
248
|
+
function resolveClaudeSettingsDirectory(runtimeEnv) {
|
|
249
|
+
const configured = runtimeEnv?.TMPDIR?.trim();
|
|
250
|
+
if (!configured || !path.isAbsolute(configured)) {
|
|
251
|
+
throw new Error('[ClaudeSandbox] managed TMPDIR is unavailable');
|
|
252
|
+
}
|
|
253
|
+
const directory = path.resolve(configured);
|
|
254
|
+
if (!isManagedSessionRuntimeDir(directory, configured)) {
|
|
255
|
+
throw new Error('[ClaudeSandbox] settings file directory is outside the managed TMPDIR');
|
|
256
|
+
}
|
|
257
|
+
return directory;
|
|
258
|
+
}
|
|
246
259
|
async function assertClaudeSettingSourcesHaveLiteralSandboxPaths(cwd, settingSources, managedSettings) {
|
|
247
260
|
if (process.platform !== 'linux' || settingSources.length === 0)
|
|
248
261
|
return;
|
|
@@ -601,6 +614,7 @@ export class AgentRunner {
|
|
|
601
614
|
activeMessageStreams = new Map();
|
|
602
615
|
interruptFns = new Map();
|
|
603
616
|
activeQueries = new Map();
|
|
617
|
+
sandboxSettingsFiles = new Map();
|
|
604
618
|
streamDone = new Map();
|
|
605
619
|
streamDoneResolvers = new Map();
|
|
606
620
|
onSessionIdUpdate;
|
|
@@ -612,6 +626,33 @@ export class AgentRunner {
|
|
|
612
626
|
/** 每个 session 最近的子进程 stderr 行(环形缓冲),用于子进程崩溃时还原真正原因 */
|
|
613
627
|
recentStderr = new Map();
|
|
614
628
|
static STDERR_BUFFER_MAX = 80;
|
|
629
|
+
trackSandboxSettingsFile(sessionId, filePath) {
|
|
630
|
+
const files = this.sandboxSettingsFiles.get(sessionId) ?? new Set();
|
|
631
|
+
files.add(filePath);
|
|
632
|
+
this.sandboxSettingsFiles.set(sessionId, files);
|
|
633
|
+
}
|
|
634
|
+
cleanupSandboxSettingsFile(sessionId, filePath) {
|
|
635
|
+
if (!filePath)
|
|
636
|
+
return;
|
|
637
|
+
try {
|
|
638
|
+
fs.rmSync(filePath, { force: true });
|
|
639
|
+
}
|
|
640
|
+
catch (error) {
|
|
641
|
+
const code = error && typeof error === 'object' && 'code' in error
|
|
642
|
+
? String(error.code)
|
|
643
|
+
: 'unknown';
|
|
644
|
+
logger.warn(`[ClaudeSandbox] failed to remove transient settings file code=${code}`);
|
|
645
|
+
}
|
|
646
|
+
const files = this.sandboxSettingsFiles.get(sessionId);
|
|
647
|
+
files?.delete(filePath);
|
|
648
|
+
if (files?.size === 0)
|
|
649
|
+
this.sandboxSettingsFiles.delete(sessionId);
|
|
650
|
+
}
|
|
651
|
+
cleanupSandboxSettingsFiles(sessionId) {
|
|
652
|
+
for (const filePath of [...(this.sandboxSettingsFiles.get(sessionId) ?? [])]) {
|
|
653
|
+
this.cleanupSandboxSettingsFile(sessionId, filePath);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
615
656
|
constructor(apiKey, model, onSessionIdUpdate, baseUrl, config) {
|
|
616
657
|
this.apiKey = apiKey;
|
|
617
658
|
this.model = model || 'sonnet';
|
|
@@ -1366,7 +1407,7 @@ export class AgentRunner {
|
|
|
1366
1407
|
* SDK 原始事件 → 标准 AgentEvent 转换
|
|
1367
1408
|
* 所有 SDK 特有的事件类型引用封装在此方法内
|
|
1368
1409
|
*/
|
|
1369
|
-
async *transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, isResuming = false) {
|
|
1410
|
+
async *transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, isResuming = false, sandboxSettingsFile) {
|
|
1370
1411
|
let lastSessionId;
|
|
1371
1412
|
let hasTurnActivity = false;
|
|
1372
1413
|
let ignoredPreTurnResult = false;
|
|
@@ -1785,6 +1826,30 @@ export class AgentRunner {
|
|
|
1785
1826
|
else {
|
|
1786
1827
|
logger.error(`[AgentRunner] Subprocess stream failed (session=${sessionId}) with no captured stderr.`);
|
|
1787
1828
|
}
|
|
1829
|
+
if (isClaudeStartupArgumentsTooLong(err)) {
|
|
1830
|
+
const detail = createClaudeStartupArgumentsTooLongError(err);
|
|
1831
|
+
await this.permissionContexts.get(sessionId)?.recordExecutionAnomaly?.({
|
|
1832
|
+
code: 'capability_unavailable',
|
|
1833
|
+
severity: 'warning',
|
|
1834
|
+
phase: 'execution',
|
|
1835
|
+
occurredAt: Date.now(),
|
|
1836
|
+
toolName: 'Bash',
|
|
1837
|
+
policyCode: 'claude_startup_arguments_too_long',
|
|
1838
|
+
decisionSource: 'infrastructure',
|
|
1839
|
+
agentAid: this.permissionContexts.get(sessionId)?.selfAid,
|
|
1840
|
+
summary: detail.message.slice(0, 512),
|
|
1841
|
+
effect: 'operation_skipped',
|
|
1842
|
+
});
|
|
1843
|
+
yield {
|
|
1844
|
+
type: 'complete',
|
|
1845
|
+
isError: true,
|
|
1846
|
+
subtype: 'infrastructure',
|
|
1847
|
+
terminalReason: 'claude_startup_arguments_too_long',
|
|
1848
|
+
errors: [detail.message],
|
|
1849
|
+
queryFinal: true,
|
|
1850
|
+
};
|
|
1851
|
+
return;
|
|
1852
|
+
}
|
|
1788
1853
|
if (isSandboxInitializationFailure(err, buf ?? [])) {
|
|
1789
1854
|
const detail = createSandboxInitializationError(err, buf ?? []);
|
|
1790
1855
|
await this.permissionContexts.get(sessionId)?.recordExecutionAnomaly?.({
|
|
@@ -1813,6 +1878,7 @@ export class AgentRunner {
|
|
|
1813
1878
|
}
|
|
1814
1879
|
finally {
|
|
1815
1880
|
this.recentStderr.delete(sessionId);
|
|
1881
|
+
this.cleanupSandboxSettingsFile(sessionId, sandboxSettingsFile);
|
|
1816
1882
|
}
|
|
1817
1883
|
}
|
|
1818
1884
|
async runQuery(sessionId, prompt, projectPath, initialClaudeSessionId, images, systemPromptAppend, sessionManager, modelOverride, runtimeEnv) {
|
|
@@ -2001,6 +2067,7 @@ export class AgentRunner {
|
|
|
2001
2067
|
userId: permCtx?.userId,
|
|
2002
2068
|
role: permCtx?.role,
|
|
2003
2069
|
permissionMode: callPermissionMode,
|
|
2070
|
+
allowReadonlySourceDiagnostics: permCtx?.allowReadonlySourceDiagnostics === true,
|
|
2004
2071
|
allowHostProcessCommands: true,
|
|
2005
2072
|
allowProtectedMetadata: false,
|
|
2006
2073
|
safeOutputHelperPath: path.join(getPackageRoot(), 'bin', 'ec-safe-output.js'),
|
|
@@ -2035,6 +2102,7 @@ export class AgentRunner {
|
|
|
2035
2102
|
peerId: permCtx?.userId,
|
|
2036
2103
|
role: permCtx?.role,
|
|
2037
2104
|
allowProtectedMetadata: false,
|
|
2105
|
+
allowReadonlySourceDiagnostics: permCtx?.allowReadonlySourceDiagnostics === true,
|
|
2038
2106
|
};
|
|
2039
2107
|
const roResult = checkReadonly(toolName, toolInput, projectPath, readonlyContext);
|
|
2040
2108
|
if (roResult.behavior === 'deny') {
|
|
@@ -2123,6 +2191,7 @@ export class AgentRunner {
|
|
|
2123
2191
|
userId: permCtx?.userId,
|
|
2124
2192
|
role: permCtx?.role,
|
|
2125
2193
|
permissionMode: callPermissionMode,
|
|
2194
|
+
allowReadonlySourceDiagnostics: permCtx?.allowReadonlySourceDiagnostics === true,
|
|
2126
2195
|
allowHostProcessCommands: true,
|
|
2127
2196
|
allowProtectedMetadata: false,
|
|
2128
2197
|
safeOutputHelperPath: path.join(getPackageRoot(), 'bin', 'ec-safe-output.js'),
|
|
@@ -2193,6 +2262,7 @@ export class AgentRunner {
|
|
|
2193
2262
|
peerId: permCtx?.userId,
|
|
2194
2263
|
role: permCtx?.role,
|
|
2195
2264
|
allowProtectedMetadata: false,
|
|
2265
|
+
allowReadonlySourceDiagnostics: permCtx?.allowReadonlySourceDiagnostics === true,
|
|
2196
2266
|
};
|
|
2197
2267
|
const roResult = checkReadonly(toolName, input, projectPath, readonlyContext);
|
|
2198
2268
|
if (roResult.behavior === 'deny') {
|
|
@@ -2362,23 +2432,54 @@ export class AgentRunner {
|
|
|
2362
2432
|
stderr: handleClaudeStderr,
|
|
2363
2433
|
env: this.getAgentEnv(runtimeEnv, sessionId, callPermissionMode)
|
|
2364
2434
|
};
|
|
2435
|
+
// Native Windows has a small CreateProcess command-line limit. The SDK
|
|
2436
|
+
// normally serializes `sandbox` into inline `--settings` JSON, so preserve
|
|
2437
|
+
// the same flag-settings semantics while passing a short file path.
|
|
2438
|
+
let sandboxSettingsFile;
|
|
2439
|
+
const shouldUseSandboxSettingsFile = process.platform === 'win32' && sandboxOptions && sandboxOptions.enabled !== false;
|
|
2440
|
+
if (shouldUseSandboxSettingsFile) {
|
|
2441
|
+
try {
|
|
2442
|
+
const managedTmpDir = resolveClaudeSettingsDirectory(runtimeEnv);
|
|
2443
|
+
sandboxSettingsFile = path.join(managedTmpDir, `.claude-sandbox-${crypto.randomUUID()}.json`);
|
|
2444
|
+
const capabilitySettings = capabilityOptions.settings;
|
|
2445
|
+
const settingsContent = JSON.stringify({
|
|
2446
|
+
...(capabilitySettings && typeof capabilitySettings === 'object' && !Array.isArray(capabilitySettings)
|
|
2447
|
+
? capabilitySettings
|
|
2448
|
+
: {}),
|
|
2449
|
+
sandbox: sandboxOptions,
|
|
2450
|
+
});
|
|
2451
|
+
fs.writeFileSync(sandboxSettingsFile, settingsContent, { flag: 'wx', mode: 0o600 });
|
|
2452
|
+
this.trackSandboxSettingsFile(sessionId, sandboxSettingsFile);
|
|
2453
|
+
logger.info(`[ClaudeSandbox] Windows settings externalized settingsChars=${settingsContent.length} ` +
|
|
2454
|
+
`settingsPathChars=${sandboxSettingsFile.length}`);
|
|
2455
|
+
}
|
|
2456
|
+
catch (error) {
|
|
2457
|
+
this.cleanupSandboxSettingsFile(sessionId, sandboxSettingsFile);
|
|
2458
|
+
throw new Error(`[ClaudeSandbox] failed to externalize Windows settings: ${error instanceof Error ? error.message : String(error)}`);
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2365
2461
|
const createQuery = (promptInput, resumeSessionId, resumeAt) => {
|
|
2366
2462
|
if (useSettingSources) {
|
|
2463
|
+
const queryOptions = {
|
|
2464
|
+
...commonOptions,
|
|
2465
|
+
...(!resumeSessionId && callSessionTitle ? { title: callSessionTitle } : {}),
|
|
2466
|
+
settingSources: [...settingSources],
|
|
2467
|
+
systemPrompt: {
|
|
2468
|
+
type: 'preset',
|
|
2469
|
+
preset: 'claude_code',
|
|
2470
|
+
...(excludeDynamic ? { excludeDynamicSections: true } : {}),
|
|
2471
|
+
...(systemPromptAppend ? { append: systemPromptAppend } : {})
|
|
2472
|
+
},
|
|
2473
|
+
...(resumeSessionId ? { resume: resumeSessionId } : {}),
|
|
2474
|
+
...(resumeAt ? { resumeSessionAt: resumeAt } : {}),
|
|
2475
|
+
};
|
|
2476
|
+
if (sandboxSettingsFile) {
|
|
2477
|
+
delete queryOptions.sandbox;
|
|
2478
|
+
queryOptions.settings = sandboxSettingsFile;
|
|
2479
|
+
}
|
|
2367
2480
|
return query({
|
|
2368
2481
|
prompt: promptInput,
|
|
2369
|
-
options:
|
|
2370
|
-
...commonOptions,
|
|
2371
|
-
...(!resumeSessionId && callSessionTitle ? { title: callSessionTitle } : {}),
|
|
2372
|
-
settingSources: [...settingSources],
|
|
2373
|
-
systemPrompt: {
|
|
2374
|
-
type: 'preset',
|
|
2375
|
-
preset: 'claude_code',
|
|
2376
|
-
...(excludeDynamic ? { excludeDynamicSections: true } : {}),
|
|
2377
|
-
...(systemPromptAppend ? { append: systemPromptAppend } : {})
|
|
2378
|
-
},
|
|
2379
|
-
...(resumeSessionId ? { resume: resumeSessionId } : {}),
|
|
2380
|
-
...(resumeAt ? { resumeSessionAt: resumeAt } : {}),
|
|
2381
|
-
}
|
|
2482
|
+
options: queryOptions
|
|
2382
2483
|
});
|
|
2383
2484
|
}
|
|
2384
2485
|
else {
|
|
@@ -2408,22 +2509,27 @@ export class AgentRunner {
|
|
|
2408
2509
|
globalClaudeMd,
|
|
2409
2510
|
systemPromptAppend,
|
|
2410
2511
|
].filter(Boolean).join('\n\n');
|
|
2512
|
+
const queryOptions = {
|
|
2513
|
+
...commonOptions,
|
|
2514
|
+
...(!resumeSessionId && callSessionTitle ? { title: callSessionTitle } : {}),
|
|
2515
|
+
settingSources: [],
|
|
2516
|
+
...(resumeSessionId ? { resume: resumeSessionId } : {}),
|
|
2517
|
+
...(resumeAt ? { resumeSessionAt: resumeAt } : {}),
|
|
2518
|
+
...(fullAppend ? {
|
|
2519
|
+
systemPrompt: {
|
|
2520
|
+
type: 'preset',
|
|
2521
|
+
preset: 'claude_code',
|
|
2522
|
+
append: fullAppend
|
|
2523
|
+
}
|
|
2524
|
+
} : {}),
|
|
2525
|
+
};
|
|
2526
|
+
if (sandboxSettingsFile) {
|
|
2527
|
+
delete queryOptions.sandbox;
|
|
2528
|
+
queryOptions.settings = sandboxSettingsFile;
|
|
2529
|
+
}
|
|
2411
2530
|
return query({
|
|
2412
2531
|
prompt: promptInput,
|
|
2413
|
-
options:
|
|
2414
|
-
...commonOptions,
|
|
2415
|
-
...(!resumeSessionId && callSessionTitle ? { title: callSessionTitle } : {}),
|
|
2416
|
-
settingSources: [],
|
|
2417
|
-
...(resumeSessionId ? { resume: resumeSessionId } : {}),
|
|
2418
|
-
...(resumeAt ? { resumeSessionAt: resumeAt } : {}),
|
|
2419
|
-
...(fullAppend ? {
|
|
2420
|
-
systemPrompt: {
|
|
2421
|
-
type: 'preset',
|
|
2422
|
-
preset: 'claude_code',
|
|
2423
|
-
append: fullAppend
|
|
2424
|
-
}
|
|
2425
|
-
} : {}),
|
|
2426
|
-
}
|
|
2532
|
+
options: queryOptions
|
|
2427
2533
|
});
|
|
2428
2534
|
}
|
|
2429
2535
|
};
|
|
@@ -2455,7 +2561,10 @@ export class AgentRunner {
|
|
|
2455
2561
|
sdkStream = createQuery(msgStream);
|
|
2456
2562
|
}
|
|
2457
2563
|
catch (error) {
|
|
2564
|
+
this.cleanupSandboxSettingsFile(sessionId, sandboxSettingsFile);
|
|
2458
2565
|
const stderr = this.recentStderr.get(sessionId) ?? [];
|
|
2566
|
+
if (isClaudeStartupArgumentsTooLong(error))
|
|
2567
|
+
throw createClaudeStartupArgumentsTooLongError(error);
|
|
2459
2568
|
if (isSandboxInitializationFailure(error, stderr))
|
|
2460
2569
|
throw createSandboxInitializationError(error, stderr);
|
|
2461
2570
|
throw error;
|
|
@@ -2468,7 +2577,10 @@ export class AgentRunner {
|
|
|
2468
2577
|
sdkStream = createQuery(msgStream, agentSessionId, resumeAt);
|
|
2469
2578
|
}
|
|
2470
2579
|
catch (error) {
|
|
2580
|
+
this.cleanupSandboxSettingsFile(sessionId, sandboxSettingsFile);
|
|
2471
2581
|
const stderr = this.recentStderr.get(sessionId) ?? [];
|
|
2582
|
+
if (isClaudeStartupArgumentsTooLong(error))
|
|
2583
|
+
throw createClaudeStartupArgumentsTooLongError(error);
|
|
2472
2584
|
if (isSandboxInitializationFailure(error, stderr))
|
|
2473
2585
|
throw createSandboxInitializationError(error, stderr);
|
|
2474
2586
|
throw error;
|
|
@@ -2483,13 +2595,14 @@ export class AgentRunner {
|
|
|
2483
2595
|
this.interruptFns.set(sessionId, () => sdkStream.interrupt());
|
|
2484
2596
|
}
|
|
2485
2597
|
// 返回标准 AgentEvent 流(重试由 MessageProcessor 层负责)
|
|
2486
|
-
const transformed = this.transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, !!agentSessionId);
|
|
2598
|
+
const transformed = this.transformStream(sdkStream, sessionId, inputId, callModel, callEffort, sdkModel, !!agentSessionId, sandboxSettingsFile);
|
|
2487
2599
|
const self = this;
|
|
2488
2600
|
return (async function* () {
|
|
2489
2601
|
try {
|
|
2490
2602
|
yield* transformed;
|
|
2491
2603
|
}
|
|
2492
2604
|
finally {
|
|
2605
|
+
self.cleanupSandboxSettingsFile(sessionId, sandboxSettingsFile);
|
|
2493
2606
|
self.streamDoneResolvers.get(sessionId)?.();
|
|
2494
2607
|
self.streamDoneResolvers.delete(sessionId);
|
|
2495
2608
|
self.streamDone.delete(sessionId);
|
|
@@ -2536,6 +2649,7 @@ export class AgentRunner {
|
|
|
2536
2649
|
}
|
|
2537
2650
|
this.interruptFns.delete(sessionId);
|
|
2538
2651
|
this.activeStreams.delete(sessionId);
|
|
2652
|
+
this.cleanupSandboxSettingsFiles(sessionId);
|
|
2539
2653
|
return { stillQueued, cancelledInputIds, closed: true };
|
|
2540
2654
|
}
|
|
2541
2655
|
hasActiveStream(sessionId) {
|
|
@@ -2553,6 +2667,7 @@ export class AgentRunner {
|
|
|
2553
2667
|
this.activeStreams.delete(sessionId);
|
|
2554
2668
|
this.interruptFns.delete(sessionId);
|
|
2555
2669
|
this.activeQueries.delete(sessionId);
|
|
2670
|
+
this.cleanupSandboxSettingsFiles(sessionId);
|
|
2556
2671
|
this.recentStderr.delete(sessionId);
|
|
2557
2672
|
}
|
|
2558
2673
|
injectUserMessage(sessionId, text) {
|
|
@@ -2657,6 +2772,7 @@ export class AgentRunner {
|
|
|
2657
2772
|
this.interruptFns.delete(sessionId);
|
|
2658
2773
|
this.activeQueries.delete(sessionId);
|
|
2659
2774
|
this.permissionContexts.delete(sessionId);
|
|
2775
|
+
this.cleanupSandboxSettingsFiles(sessionId);
|
|
2660
2776
|
}
|
|
2661
2777
|
async dispose() {
|
|
2662
2778
|
const interrupts = [...this.interruptFns.values()].map(async (interrupt) => {
|
|
@@ -2686,6 +2802,9 @@ export class AgentRunner {
|
|
|
2686
2802
|
this.streamDoneResolvers.clear();
|
|
2687
2803
|
this.permissionContexts.clear();
|
|
2688
2804
|
this.recentStderr.clear();
|
|
2805
|
+
for (const sessionId of [...this.sandboxSettingsFiles.keys()]) {
|
|
2806
|
+
this.cleanupSandboxSettingsFiles(sessionId);
|
|
2807
|
+
}
|
|
2689
2808
|
}
|
|
2690
2809
|
resolveSessionFile(agentSessionId, projectPath) {
|
|
2691
2810
|
const encodedProjectPath = encodePath(projectPath);
|
|
@@ -13,7 +13,7 @@ import { CodexAppServerClient } from './codex-app-server-client.js';
|
|
|
13
13
|
import { resolveOpenaiConfig } from './baseagent.js';
|
|
14
14
|
import { logger } from '../utils/logger.js';
|
|
15
15
|
import { summarizeToolInputForAudit } from '../utils/tool-summary.js';
|
|
16
|
-
import { auditToolPreflightDenial } from '../core/auth/authorization-audit.js';
|
|
16
|
+
import { auditCodexApprovalDecision, auditToolPreflightDenial } from '../core/auth/authorization-audit.js';
|
|
17
17
|
import { isRetryableError } from '../utils/error-utils.js';
|
|
18
18
|
import { renderActionAsText } from '../core/interaction-router.js';
|
|
19
19
|
import { buildEnvelope, sendInteractionPayload } from '../core/message/message-utils.js';
|
|
@@ -1694,7 +1694,7 @@ export class CodexRunner {
|
|
|
1694
1694
|
logger.warn(`[CodexRunner] failed to record policy-hook denial: session=${sessionKey} tool=${toolName} error=${error instanceof Error ? error.message : String(error)}`);
|
|
1695
1695
|
}
|
|
1696
1696
|
const response = this.toAppServerApprovalResponse(request.method, 'deny', toolInput, unattended);
|
|
1697
|
-
this.logAppServerApprovalAudit(request, sessionKey, toolName, 'deny', 'policy', policyResult.policyCode ?? 'session_policy_hook');
|
|
1697
|
+
this.logAppServerApprovalAudit(request, sessionKey, toolName, 'deny', 'policy', policyResult.policyCode ?? 'session_policy_hook', policyResult.reason ?? 'session policy denied approval');
|
|
1698
1698
|
return response;
|
|
1699
1699
|
}
|
|
1700
1700
|
const summary = this.summarizeAppServerRequest(request.method, params);
|
|
@@ -1703,7 +1703,7 @@ export class CodexRunner {
|
|
|
1703
1703
|
if (!workspacePath) {
|
|
1704
1704
|
logger.warn(`[CodexRunner] approval denied because thread workspace is unknown: method=${request.method} thread=${params.threadId ?? params.conversationId ?? '<missing>'}`);
|
|
1705
1705
|
const response = this.toAppServerApprovalResponse(request.method, 'deny', toolInput, unattended);
|
|
1706
|
-
this.logAppServerApprovalAudit(request, sessionKey, toolName, 'deny', 'infrastructure', 'approval_workspace_unknown');
|
|
1706
|
+
this.logAppServerApprovalAudit(request, sessionKey, toolName, 'deny', 'infrastructure', 'approval_workspace_unknown', 'thread workspace is unknown');
|
|
1707
1707
|
return response;
|
|
1708
1708
|
}
|
|
1709
1709
|
const operationCwd = this.resolvePermissionOperationCwd(params, workspacePath, toolName);
|
|
@@ -1738,7 +1738,7 @@ export class CodexRunner {
|
|
|
1738
1738
|
logger.warn(`[CodexRunner] failed to record infrastructure approval denial: session=${sessionKey} code=${policyCode} error=${error instanceof Error ? error.message : String(error)}`);
|
|
1739
1739
|
}
|
|
1740
1740
|
const response = this.toAppServerApprovalResponse(request.method, 'deny', toolInput, unattended);
|
|
1741
|
-
this.logAppServerApprovalAudit(request, sessionKey, toolName, 'deny', 'infrastructure', policyCode);
|
|
1741
|
+
this.logAppServerApprovalAudit(request, sessionKey, toolName, 'deny', 'infrastructure', policyCode, message);
|
|
1742
1742
|
return response;
|
|
1743
1743
|
};
|
|
1744
1744
|
if (managedEcIntent && !approvedCommand) {
|
|
@@ -1763,7 +1763,13 @@ export class CodexRunner {
|
|
|
1763
1763
|
}
|
|
1764
1764
|
}
|
|
1765
1765
|
const response = this.toAppServerApprovalResponse(request.method, decision, toolInput, unattended);
|
|
1766
|
-
this.logAppServerApprovalAudit(request, sessionKey, toolName, decision === 'deny' ? 'deny' : 'allow', decision === 'deny' ? (sessionMode === 'auto' || sessionMode === 'readonly' ? 'policy' : 'approval') : 'approval')
|
|
1766
|
+
this.logAppServerApprovalAudit(request, sessionKey, toolName, decision === 'deny' ? 'deny' : 'allow', decision === 'deny' ? (sessionMode === 'auto' || sessionMode === 'readonly' ? 'policy' : 'approval') : 'approval', decision === 'deny' && (sessionMode === 'auto' || sessionMode === 'readonly')
|
|
1767
|
+
? 'permission_mode_denied'
|
|
1768
|
+
: undefined, decision === 'deny'
|
|
1769
|
+
? (reason || (sessionMode === 'auto' || sessionMode === 'readonly'
|
|
1770
|
+
? `permission mode ${sessionMode} denied approval`
|
|
1771
|
+
: 'user approval denied'))
|
|
1772
|
+
: 'approval accepted');
|
|
1767
1773
|
logger.info(`[CodexRunner] app-server approval response id=${request.id} method=${request.method} decision=${decision} response=${JSON.stringify(response)}`);
|
|
1768
1774
|
return response;
|
|
1769
1775
|
}
|
|
@@ -1773,7 +1779,21 @@ export class CodexRunner {
|
|
|
1773
1779
|
throw error;
|
|
1774
1780
|
}
|
|
1775
1781
|
}
|
|
1776
|
-
logAppServerApprovalAudit(request, sessionKey, toolName, decision, decisionSource, policyCode) {
|
|
1782
|
+
logAppServerApprovalAudit(request, sessionKey, toolName, decision, decisionSource, policyCode, reason) {
|
|
1783
|
+
const context = this.permissionContexts.get(sessionKey);
|
|
1784
|
+
auditCodexApprovalDecision({
|
|
1785
|
+
requestId: request.id !== undefined ? String(request.id) : undefined,
|
|
1786
|
+
method: request.method,
|
|
1787
|
+
sessionId: sessionKey,
|
|
1788
|
+
agentAid: context?.selfAid,
|
|
1789
|
+
toolName,
|
|
1790
|
+
decision,
|
|
1791
|
+
decisionSource,
|
|
1792
|
+
policyCode,
|
|
1793
|
+
permissionMode: normalizePermissionMode(this.chatModes.get(sessionKey) ?? this.currentMode).mode,
|
|
1794
|
+
role: context?.role,
|
|
1795
|
+
reason: reason ?? policyCode ?? (decision === 'allow' ? 'approval accepted' : 'approval denied'),
|
|
1796
|
+
});
|
|
1777
1797
|
logger.info(`[CodexRunner] app-server approval audit ${JSON.stringify({
|
|
1778
1798
|
requestId: request.id !== undefined ? String(request.id) : undefined,
|
|
1779
1799
|
method: request.method,
|
|
@@ -1782,6 +1802,7 @@ export class CodexRunner {
|
|
|
1782
1802
|
decision,
|
|
1783
1803
|
decisionSource,
|
|
1784
1804
|
...(policyCode ? { policyCode } : {}),
|
|
1805
|
+
reason: reason ?? policyCode ?? (decision === 'allow' ? 'approval accepted' : 'approval denied'),
|
|
1785
1806
|
executed: false,
|
|
1786
1807
|
})}`);
|
|
1787
1808
|
}
|
|
@@ -2200,6 +2221,7 @@ export class CodexRunner {
|
|
|
2200
2221
|
// readonly decision. The map entry alone is stale if the directory was
|
|
2201
2222
|
// replaced between preflight and the approval callback.
|
|
2202
2223
|
managedTempDir: this.getManagedTempDir(sessionKey),
|
|
2224
|
+
allowReadonlySourceDiagnostics: permCtx?.allowReadonlySourceDiagnostics === true,
|
|
2203
2225
|
} : undefined;
|
|
2204
2226
|
if (toolName === 'Bash')
|
|
2205
2227
|
return checkReadonly(toolName, input, projectPath, readonlyContext);
|
|
@@ -2315,12 +2337,12 @@ export class CodexRunner {
|
|
|
2315
2337
|
// per-session 权限模式(runQuery 写入);缺省回落实例级 currentMode(兼容无 runQuery 上下文的调用)
|
|
2316
2338
|
const rawMode = this.chatModes.get(sessionKey) ?? this.currentMode;
|
|
2317
2339
|
const mode = normalizePermissionMode(rawMode).mode;
|
|
2318
|
-
const recordBlockedOperation = async (policyCode, operationInput = toolInput) => {
|
|
2340
|
+
const recordBlockedOperation = async (policyCode, operationInput = toolInput, denialReason = 'policy denied') => {
|
|
2319
2341
|
const summary = summarizeToolInputForAudit(toolName, operationInput).slice(0, 512);
|
|
2320
2342
|
auditToolPreflightDenial({
|
|
2321
2343
|
toolName,
|
|
2322
2344
|
policyCode,
|
|
2323
|
-
reason:
|
|
2345
|
+
reason: denialReason,
|
|
2324
2346
|
summary,
|
|
2325
2347
|
sessionId: sessionKey,
|
|
2326
2348
|
agentAid: permissionContext?.selfAid,
|
|
@@ -2364,6 +2386,7 @@ export class CodexRunner {
|
|
|
2364
2386
|
userId: permissionContext?.userId,
|
|
2365
2387
|
role: permissionContext?.role,
|
|
2366
2388
|
permissionMode: mode,
|
|
2389
|
+
allowReadonlySourceDiagnostics: permissionContext?.allowReadonlySourceDiagnostics === true,
|
|
2367
2390
|
projectPath: operationCwd,
|
|
2368
2391
|
workspacePath,
|
|
2369
2392
|
});
|
|
@@ -2371,7 +2394,7 @@ export class CodexRunner {
|
|
|
2371
2394
|
logger.warn(`[CodexRunner] tool preflight denied: session=${sessionKey} tool=${toolName} ` +
|
|
2372
2395
|
`policy=${preflight.policyCode ?? 'unknown'} reason=${preflight.message ?? 'policy denied'}`);
|
|
2373
2396
|
if (preflight.policyCode) {
|
|
2374
|
-
await recordBlockedOperation(preflight.policyCode);
|
|
2397
|
+
await recordBlockedOperation(preflight.policyCode, toolInput, preflight.message ?? 'tool preflight denied');
|
|
2375
2398
|
}
|
|
2376
2399
|
return 'deny';
|
|
2377
2400
|
}
|
|
@@ -2399,7 +2422,7 @@ export class CodexRunner {
|
|
|
2399
2422
|
if (mode === 'readonly') {
|
|
2400
2423
|
const readonly = this.checkCodexReadonly(toolName, checkedInput, operationCwd, sessionKey);
|
|
2401
2424
|
if (readonly.behavior === 'deny') {
|
|
2402
|
-
await recordBlockedOperation(readonly.policyCode ?? 'readonly_mode', checkedInput);
|
|
2425
|
+
await recordBlockedOperation(readonly.policyCode ?? 'readonly_mode', checkedInput, readonly.message);
|
|
2403
2426
|
return 'deny';
|
|
2404
2427
|
}
|
|
2405
2428
|
return 'allow';
|
|
@@ -2928,6 +2951,7 @@ export class CodexRunner {
|
|
|
2928
2951
|
name: 'Shell',
|
|
2929
2952
|
result: item.aggregatedOutput ?? '',
|
|
2930
2953
|
isError: item.exitCode !== null && item.exitCode !== undefined ? item.exitCode !== 0 : item.status === 'failed',
|
|
2954
|
+
...(item.error?.code || item.error?.errorCode ? { errorCode: String(item.error.code ?? item.error.errorCode) } : {}),
|
|
2931
2955
|
callId: item.id,
|
|
2932
2956
|
durationMs: typeof item.durationMs === 'number' ? item.durationMs : undefined,
|
|
2933
2957
|
};
|
|
@@ -2939,6 +2963,7 @@ export class CodexRunner {
|
|
|
2939
2963
|
result: item.result,
|
|
2940
2964
|
isError: item.status === 'failed',
|
|
2941
2965
|
error: item.error?.message,
|
|
2966
|
+
...(item.error?.code || item.error?.errorCode ? { errorCode: String(item.error.code ?? item.error.errorCode) } : {}),
|
|
2942
2967
|
callId: item.id,
|
|
2943
2968
|
durationMs: typeof item.durationMs === 'number' ? item.durationMs : undefined,
|
|
2944
2969
|
};
|
|
@@ -2949,6 +2974,7 @@ export class CodexRunner {
|
|
|
2949
2974
|
name: item.namespace ? `${item.namespace}:${item.tool}` : item.tool,
|
|
2950
2975
|
result: item.contentItems,
|
|
2951
2976
|
isError: item.success === false || item.status === 'failed',
|
|
2977
|
+
...(item.error?.code || item.error?.errorCode ? { errorCode: String(item.error.code ?? item.error.errorCode) } : {}),
|
|
2952
2978
|
callId: item.id,
|
|
2953
2979
|
durationMs: typeof item.durationMs === 'number' ? item.durationMs : undefined,
|
|
2954
2980
|
};
|
|
@@ -924,7 +924,8 @@ export class EcagentRunner {
|
|
|
924
924
|
if (event.type === 'tool_execution_end') {
|
|
925
925
|
queue.push({
|
|
926
926
|
type: 'tool_result', name: event.toolName, result: extractToolResult(event.result),
|
|
927
|
-
isError: event.isError, error: event.isError ? extractToolResult(event.result) : undefined,
|
|
927
|
+
isError: event.isError, error: event.isError ? extractToolResult(event.result) : undefined,
|
|
928
|
+
callId: event.toolCallId,
|
|
928
929
|
});
|
|
929
930
|
return;
|
|
930
931
|
}
|
|
@@ -1006,6 +1007,7 @@ export class EcagentRunner {
|
|
|
1006
1007
|
userId: permissionContext?.userId,
|
|
1007
1008
|
role: permissionContext?.role,
|
|
1008
1009
|
permissionMode: mode,
|
|
1010
|
+
allowReadonlySourceDiagnostics: permissionContext?.allowReadonlySourceDiagnostics === true,
|
|
1009
1011
|
projectPath,
|
|
1010
1012
|
});
|
|
1011
1013
|
if (preflight.behavior === 'deny') {
|
|
@@ -1020,6 +1022,7 @@ export class EcagentRunner {
|
|
|
1020
1022
|
if (mode === 'readonly') {
|
|
1021
1023
|
const decision = checkReadonly(toolName, checkedInput, projectPath, {
|
|
1022
1024
|
sessionId, channel: permissionContext?.channel, peerId: permissionContext?.userId, role: permissionContext?.role,
|
|
1025
|
+
allowReadonlySourceDiagnostics: permissionContext?.allowReadonlySourceDiagnostics === true,
|
|
1023
1026
|
});
|
|
1024
1027
|
if (decision.behavior === 'deny') {
|
|
1025
1028
|
await recordBlockedOperation(decision.policyCode ?? 'readonly_mode', checkedInput);
|
package/dist/aun/msg/group.js
CHANGED
|
@@ -9,7 +9,7 @@ import { daemonTaskSendIpcTimeoutMs, resolveAunMessageEncrypt } from './p2p.js';
|
|
|
9
9
|
import { checkGroupIndex, getGroupIndex } from './group-index.js';
|
|
10
10
|
import { appendMessageLog, buildOutboundEntry, classifyAunPayloadForLog } from '../../core/message/message-log.js';
|
|
11
11
|
import { chatDirPath } from '../../core/session/session-fs-store.js';
|
|
12
|
-
import { readBestTaskRuntimeContext } from '../../cli/task-context.js';
|
|
12
|
+
import { normalizeCodexTextPayload, readBestTaskRuntimeContext } from '../../cli/task-context.js';
|
|
13
13
|
import { ipcQuery } from '../../ipc.js';
|
|
14
14
|
import { resolvePaths } from '../../paths.js';
|
|
15
15
|
import { AGENT_DELEGATION_TOKEN_ENV } from '../../core/auth/agent-delegation.js';
|
|
@@ -151,6 +151,7 @@ export async function groupSend(args) {
|
|
|
151
151
|
const runtimeContext = await readBestTaskRuntimeContext();
|
|
152
152
|
if (args.body.mode !== 'file') {
|
|
153
153
|
const payload = buildGroupPayload(args.body);
|
|
154
|
+
normalizeCodexTextPayload(payload, runtimeContext);
|
|
154
155
|
applyGroupRoutingPayloadFields(payload, args, runtimeContext);
|
|
155
156
|
const daemonResult = await tryDaemonGroupSend(args, payload, runtimeContext);
|
|
156
157
|
if (daemonResult)
|
|
@@ -192,6 +193,7 @@ export async function groupSend(args) {
|
|
|
192
193
|
break;
|
|
193
194
|
}
|
|
194
195
|
}
|
|
196
|
+
normalizeCodexTextPayload(payload, runtimeContext);
|
|
195
197
|
const payloadMentions = Object.prototype.hasOwnProperty.call(payload, 'mentions')
|
|
196
198
|
? normalizeAunMentionEntries(payload.mentions)
|
|
197
199
|
: undefined;
|
package/dist/aun/msg/p2p.js
CHANGED
|
@@ -4,27 +4,27 @@ import { createShortConnection } from '../rpc/index.js';
|
|
|
4
4
|
import { getAidStore, SLOT } from '../aid/store.js';
|
|
5
5
|
import { uploadFileAndBuildPayload } from './upload.js';
|
|
6
6
|
import { appendMessageLog, buildOutboundEntry, classifyAunPayloadForLog } from '../../core/message/message-log.js';
|
|
7
|
-
import { readBestTaskRuntimeContext, runtimeRefMessageIdForMsgSend } from '../../cli/task-context.js';
|
|
7
|
+
import { normalizeCodexTextPayload, readBestTaskRuntimeContext, runtimeRefMessageIdForMsgSend } from '../../cli/task-context.js';
|
|
8
8
|
import { chatDirPath } from '../../core/session/session-fs-store.js';
|
|
9
9
|
import { resolvePaths } from '../../paths.js';
|
|
10
10
|
import { ipcQuery } from '../../ipc.js';
|
|
11
11
|
import { AGENT_DELEGATION_TOKEN_ENV } from '../../core/auth/agent-delegation.js';
|
|
12
12
|
import { loadDaemonConfig } from '../../config-store.js';
|
|
13
13
|
import { normalizeAunMentionEntries } from './mention-schema.js';
|
|
14
|
-
const DEFAULT_TASK_EXECUTION_MS = 60 * 60 * 1000;
|
|
15
14
|
const DAEMON_SEND_TIMEOUT_GRACE_MS = 10_000;
|
|
15
|
+
const DEFAULT_IDLE_TIMEOUT_MS = 120_000;
|
|
16
16
|
const MAX_TIMER_MS = 2_147_483_647;
|
|
17
17
|
export function daemonTaskSendIpcTimeoutMs(file = false) {
|
|
18
|
-
let
|
|
18
|
+
let idleTimeoutMs = DEFAULT_IDLE_TIMEOUT_MS;
|
|
19
19
|
try {
|
|
20
20
|
const config = JSON.parse(fs.readFileSync(resolvePaths().daemonConfig, 'utf8'));
|
|
21
|
-
const seconds = config.idleMonitor?.
|
|
21
|
+
const seconds = config.idleMonitor?.timeout;
|
|
22
22
|
if (typeof seconds === 'number' && Number.isFinite(seconds) && seconds > 0) {
|
|
23
|
-
|
|
23
|
+
idleTimeoutMs = seconds * 1000;
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
catch { }
|
|
27
|
-
return Math.min(MAX_TIMER_MS, Math.max(file ? 120_000 : 5_000,
|
|
27
|
+
return Math.min(MAX_TIMER_MS, Math.max(file ? 120_000 : 5_000, idleTimeoutMs + DAEMON_SEND_TIMEOUT_GRACE_MS));
|
|
28
28
|
}
|
|
29
29
|
export function resolveAunMessageEncrypt(explicit) {
|
|
30
30
|
if (typeof explicit === 'boolean')
|
|
@@ -207,6 +207,7 @@ export async function msgSend(args) {
|
|
|
207
207
|
let payload;
|
|
208
208
|
if (args.body.mode !== 'file') {
|
|
209
209
|
payload = buildSimplePayload(args.body);
|
|
210
|
+
normalizeCodexTextPayload(payload, runtimeContext);
|
|
210
211
|
if (Object.prototype.hasOwnProperty.call(payload, 'mentions')) {
|
|
211
212
|
payload.mentions = normalizeAunMentionEntries(payload.mentions);
|
|
212
213
|
}
|
|
@@ -263,6 +264,7 @@ export async function msgSend(args) {
|
|
|
263
264
|
// 3. 构建 payload
|
|
264
265
|
if (!payload) {
|
|
265
266
|
payload = buildSimplePayload(args.body);
|
|
267
|
+
normalizeCodexTextPayload(payload, runtimeContext);
|
|
266
268
|
}
|
|
267
269
|
// 4. 写入 payload.chatmode
|
|
268
270
|
payload.chatmode = chatmode;
|