evolcore 0.0.16 → 0.0.18
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 +46 -0
- package/bin/codex-managed-hook.mjs +16 -7
- package/bin/install-codex-managed-hooks.mjs +4 -2
- package/dist/agents/claude-runner.js +132 -14
- package/dist/agents/codex-app-server-client.js +6 -1
- package/dist/agents/codex-runner.js +21 -6
- package/dist/agents/ecagent-runner.js +39 -10
- package/dist/agents/gemini-runner.js +90 -19
- package/dist/aun/aid/store.js +36 -0
- package/dist/aun/msg/group.js +3 -1
- package/dist/aun/msg/p2p.js +23 -9
- package/dist/channels/aun.js +159 -21
- package/dist/cli/agent-command.js +67 -6
- package/dist/cli/agent.js +26 -0
- package/dist/cli/command-log.js +23 -4
- package/dist/cli/daemon-commands.js +53 -12
- package/dist/cli/init.js +21 -5
- package/dist/cli/restart-monitor.js +13 -6
- package/dist/cli/task-context.js +46 -1
- package/dist/cli/watch-logs.js +2 -2
- package/dist/config/builtin-roles.js +5 -1
- package/dist/config/role-ranks.js +4 -0
- package/dist/core/audit/event-key.js +29 -0
- package/dist/core/audit/log-integrity.js +13 -3
- package/dist/core/auth/auth-gateway.js +14 -18
- package/dist/core/auth/authorization-audit.js +110 -3
- package/dist/core/auth/authorization-denial.js +17 -0
- package/dist/core/auth/operation-authorizer.js +143 -18
- package/dist/core/auth/operation-catalog.js +21 -5
- package/dist/core/bootstrap-messages.js +11 -6
- package/dist/core/bootstrap-service.js +26 -4
- package/dist/core/causation/aun-association.js +7 -4
- package/dist/core/command/agent-control.js +25 -16
- package/dist/core/command/command-handler.js +50 -4
- package/dist/core/command/group-menu.js +1 -1
- package/dist/core/command/menu-catalog.js +32 -7
- package/dist/core/command/menu-handler.js +59 -23
- package/dist/core/command/menu-protocol.js +196 -0
- package/dist/core/command/slash-gate.js +14 -5
- package/dist/core/command/slash-handler.js +81 -99
- package/dist/core/event-catalog.js +18 -0
- package/dist/core/message/message-bridge.js +72 -9
- package/dist/core/message/pause-controller.js +53 -0
- package/dist/core/message/response-engine.js +98 -11
- package/dist/core/permission/sandbox-runtime.js +79 -13
- package/dist/core/permission/tool-policy.js +1 -1
- package/dist/index.js +357 -48
- package/dist/ipc.js +75 -4
- package/dist/utils/atomic-write.js +45 -11
- package/dist/utils/error-utils.js +38 -0
- package/dist/utils/logger.js +27 -0
- package/dist/utils/windows-autostart.js +740 -83
- package/ecagent/dist/harness/agent-harness.d.ts +1 -1
- package/ecagent/dist/harness/agent-harness.js +6 -4
- package/kits/docs/evolcore/config.md +1 -1
- package/kits/docs/evolcore/group-rules.md +2 -1
- package/kits/docs/identity/ROLE_DETAIL.md +3 -1
- package/kits/eck_manifest.json +25 -16
- package/kits/rules/01-overview.md +5 -5
- package/kits/rules/03-identity.md +1 -1
- package/kits/rules/04-relation.md +4 -4
- package/kits/rules/05-venue.md +5 -5
- package/kits/templates/bootstrap-welcome.md +3 -1
- package/kits/templates/system-fragments/bootstrap.md +17 -9
- package/package.json +1 -1
|
@@ -129,44 +129,99 @@ const GEMINI_L_CLASS_PATTERNS = [
|
|
|
129
129
|
const GEMINI_L_CLASS_WRITE_TOOLS = [
|
|
130
130
|
'write_file', 'replace', 'delete_file', 'move_file', 'rename_file',
|
|
131
131
|
];
|
|
132
|
+
const GEMINI_BOOTSTRAP_FILE_TOOLS = ['write_file', 'replace'];
|
|
133
|
+
const GEMINI_BOOTSTRAP_LIFECYCLE_TOOLS = ['update_topic', 'complete_task'];
|
|
132
134
|
const GEMINI_EC_COMMAND_PATTERNS = [
|
|
133
|
-
String.raw
|
|
135
|
+
String.raw `\x00"command":"[ ]*ec[ ]*"\x00`,
|
|
134
136
|
// This is only a shell-process boundary. It deliberately does not inspect
|
|
135
137
|
// subcommands or arguments. Shell composition remains excluded because the
|
|
136
138
|
// Gemini policy exception applies to the whole run_shell_command call.
|
|
137
|
-
|
|
139
|
+
// stableStringify JSON-escapes control characters. Reject those escapes
|
|
140
|
+
// explicitly, while retaining valid `\\`/`\"` escapes used by paths and
|
|
141
|
+
// quoted arguments.
|
|
142
|
+
String.raw `\x00"command":"[ ]*ec[ ]+(?:[^"\x00;&|$()<>\x60\r\n\\]|\\(?:\\|"))*"\x00`,
|
|
138
143
|
];
|
|
139
|
-
export function resolveGeminiPermissionProfile(value) {
|
|
144
|
+
export function resolveGeminiPermissionProfile(value, bootstrap = false) {
|
|
140
145
|
const normalized = normalizePermissionMode(value);
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
146
|
+
const profile = normalized.mode === 'auto'
|
|
147
|
+
? { mode: 'auto', approvalMode: 'auto_edit' }
|
|
148
|
+
: normalized.mode === 'request' || normalized.mode === 'bypass'
|
|
149
|
+
? { mode: 'readonly', approvalMode: 'plan', degradedFrom: normalized.mode }
|
|
150
|
+
: { mode: 'readonly', approvalMode: 'plan' };
|
|
151
|
+
// Bootstrap has its own deny-by-default Admin policy. auto_edit is required
|
|
152
|
+
// so Gemini exposes write_file/replace, while that policy still limits them
|
|
153
|
+
// to the single declared agent.md path.
|
|
154
|
+
return bootstrap ? { ...profile, approvalMode: 'auto_edit' } : profile;
|
|
155
|
+
}
|
|
156
|
+
function escapeGeminiArgsRegex(value) {
|
|
157
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
158
|
+
}
|
|
159
|
+
function normalizeGeminiBootstrapAgentMdPath(candidate) {
|
|
160
|
+
const filePath = path.resolve(candidate);
|
|
161
|
+
const aidDir = path.dirname(filePath);
|
|
162
|
+
const aid = path.basename(aidDir);
|
|
163
|
+
const aidsDirName = path.basename(path.dirname(aidDir));
|
|
164
|
+
if (path.basename(filePath).toLowerCase() !== 'agent.md'
|
|
165
|
+
|| aidsDirName.toLowerCase() !== 'aids'
|
|
166
|
+
|| !/^[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?){2,}$/.test(aid)) {
|
|
167
|
+
throw new Error(`Invalid Gemini bootstrap agent.md path: ${candidate}`);
|
|
168
|
+
}
|
|
169
|
+
return { filePath, aid };
|
|
170
|
+
}
|
|
171
|
+
function geminiJsonFieldEqualsPattern(fields, value) {
|
|
172
|
+
const encodedValue = JSON.stringify(value);
|
|
173
|
+
return `\\x00"(?:${fields.join('|')})":${escapeGeminiArgsRegex(encodedValue)}\\x00`;
|
|
174
|
+
}
|
|
175
|
+
function geminiExactShellCommandPattern(command) {
|
|
176
|
+
// Gemini stableStringify wraps the top-level NUL-delimited field in `{}`.
|
|
177
|
+
// Matching the whole object also rejects extra shell options such as
|
|
178
|
+
// is_background or additional_permissions during bootstrap.
|
|
179
|
+
return `^\\{\\x00"command":"[ ]*${escapeGeminiArgsRegex(command)}[ ]*"\\x00\\}$`;
|
|
147
180
|
}
|
|
148
181
|
function appendGeminiPolicyRule(lines, toolName, decision, priority, argsPattern) {
|
|
149
182
|
lines.push('[[rule]]');
|
|
150
183
|
lines.push(`toolName = ${Array.isArray(toolName) ? JSON.stringify(toolName) : JSON.stringify(toolName)}`);
|
|
151
184
|
if (argsPattern)
|
|
152
|
-
lines.push(`argsPattern =
|
|
185
|
+
lines.push(`argsPattern = ${JSON.stringify(argsPattern)}`);
|
|
153
186
|
lines.push(`decision = "${decision}"`);
|
|
154
187
|
lines.push(`priority = ${priority}`, '');
|
|
155
188
|
}
|
|
156
|
-
export function buildGeminiAdminPolicy(profile) {
|
|
189
|
+
export function buildGeminiAdminPolicy(profile, bootstrapAgentMdPath) {
|
|
157
190
|
const lines = [];
|
|
191
|
+
if (bootstrapAgentMdPath) {
|
|
192
|
+
const bootstrap = normalizeGeminiBootstrapAgentMdPath(bootstrapAgentMdPath);
|
|
193
|
+
// Preserve the global protected-path boundary even if a malformed internal
|
|
194
|
+
// caller ever supplies a protected file as the bootstrap target.
|
|
195
|
+
for (const pattern of GEMINI_H_CLASS_PATTERNS) {
|
|
196
|
+
appendGeminiPolicyRule(lines, '*', 'deny', 998, pattern);
|
|
197
|
+
}
|
|
198
|
+
for (const pattern of GEMINI_L_CLASS_PATTERNS) {
|
|
199
|
+
appendGeminiPolicyRule(lines, GEMINI_L_CLASS_WRITE_TOOLS, 'deny', 997, pattern);
|
|
200
|
+
}
|
|
201
|
+
const readPathPattern = geminiJsonFieldEqualsPattern(['file_path'], bootstrap.filePath);
|
|
202
|
+
const writePathPattern = geminiJsonFieldEqualsPattern(['file_path'], bootstrap.filePath);
|
|
203
|
+
appendGeminiPolicyRule(lines, 'read_file', 'allow', 990, readPathPattern);
|
|
204
|
+
appendGeminiPolicyRule(lines, GEMINI_BOOTSTRAP_FILE_TOOLS, 'allow', 990, writePathPattern);
|
|
205
|
+
appendGeminiPolicyRule(lines, 'run_shell_command', 'allow', 990, geminiExactShellCommandPattern(`ec aid agentmd put ${bootstrap.aid}`));
|
|
206
|
+
appendGeminiPolicyRule(lines, 'run_shell_command', 'allow', 990, geminiExactShellCommandPattern(`ec agent ready ${bootstrap.aid}`));
|
|
207
|
+
// These are Gemini's non-I/O task bookkeeping tools. They do not expose
|
|
208
|
+
// project files, shell, network, MCP, or user-interaction capabilities.
|
|
209
|
+
appendGeminiPolicyRule(lines, GEMINI_BOOTSTRAP_LIFECYCLE_TOOLS, 'allow', 950);
|
|
210
|
+
appendGeminiPolicyRule(lines, '*', 'deny', 900);
|
|
211
|
+
return lines.join('\n');
|
|
212
|
+
}
|
|
158
213
|
// EC/daemon owns all command semantics and authorization. The Runner only
|
|
159
214
|
// admits a single EC process through the otherwise mode-specific shell
|
|
160
215
|
// policy. Keep this above path denies so `--file` is not reinterpreted here.
|
|
161
216
|
for (const pattern of GEMINI_EC_COMMAND_PATTERNS) {
|
|
162
|
-
appendGeminiPolicyRule(lines, 'run_shell_command', 'allow',
|
|
217
|
+
appendGeminiPolicyRule(lines, 'run_shell_command', 'allow', 999, pattern);
|
|
163
218
|
}
|
|
164
219
|
// H-class protection is mode-independent and applies to built-in and MCP tools.
|
|
165
220
|
for (const pattern of GEMINI_H_CLASS_PATTERNS) {
|
|
166
|
-
appendGeminiPolicyRule(lines, '*', 'deny',
|
|
221
|
+
appendGeminiPolicyRule(lines, '*', 'deny', 998, pattern);
|
|
167
222
|
}
|
|
168
223
|
for (const pattern of GEMINI_L_CLASS_PATTERNS) {
|
|
169
|
-
appendGeminiPolicyRule(lines, GEMINI_L_CLASS_WRITE_TOOLS, 'deny',
|
|
224
|
+
appendGeminiPolicyRule(lines, GEMINI_L_CLASS_WRITE_TOOLS, 'deny', 997, pattern);
|
|
170
225
|
}
|
|
171
226
|
if (profile.mode === 'readonly') {
|
|
172
227
|
appendGeminiPolicyRule(lines, GEMINI_READONLY_TOOLS, 'allow', 950);
|
|
@@ -277,7 +332,10 @@ export class GeminiRunner {
|
|
|
277
332
|
let geminiSessionId = initialAgentSessionId || this.activeSessions.get(sessionId);
|
|
278
333
|
// per-call 权限模式/模型:优先 override,缺省回落实例级(多会话并发互不污染)
|
|
279
334
|
const requestedPermissionMode = modelOverride?.permissionMode || this.currentMode;
|
|
280
|
-
const
|
|
335
|
+
const bootstrapAgentMdPath = modelOverride?.bootstrapAgentMdPath
|
|
336
|
+
? normalizeGeminiBootstrapAgentMdPath(modelOverride.bootstrapAgentMdPath).filePath
|
|
337
|
+
: undefined;
|
|
338
|
+
const permissionProfile = resolveGeminiPermissionProfile(requestedPermissionMode, !!bootstrapAgentMdPath);
|
|
281
339
|
const callModel = modelOverride?.model || this.model;
|
|
282
340
|
const runtimeDir = runtimeEnv?.EVOLCORE_SESSION_RUNTIME_DIR;
|
|
283
341
|
const managedTmpDir = runtimeDir ?? process.env.TMPDIR;
|
|
@@ -288,7 +346,12 @@ export class GeminiRunner {
|
|
|
288
346
|
fs.mkdirSync(geminiHome, { recursive: true, mode: 0o700 });
|
|
289
347
|
const sandboxProbe = buildBubblewrapCommand(this.resolved.cliPath, [], {
|
|
290
348
|
projectPath,
|
|
291
|
-
writablePaths: [
|
|
349
|
+
writablePaths: [
|
|
350
|
+
geminiHome,
|
|
351
|
+
...(runtimeDir ? [runtimeDir] : []),
|
|
352
|
+
...(runtimeLockDir ? [runtimeLockDir] : []),
|
|
353
|
+
...(bootstrapAgentMdPath ? [bootstrapAgentMdPath] : []),
|
|
354
|
+
],
|
|
292
355
|
});
|
|
293
356
|
if (!sandboxProbe && workspaceContainsHClassPaths(projectPath)) {
|
|
294
357
|
throw new Error('Gemini 缺少可用的路径级隔离运行时,且项目覆盖 EvolCore 受保护根;已拒绝启动本轮任务');
|
|
@@ -309,7 +372,7 @@ export class GeminiRunner {
|
|
|
309
372
|
throw new Error('Gemini 系统级 admin policy 会忽略 EvolCore 的临时安全策略,已拒绝启动本轮任务');
|
|
310
373
|
}
|
|
311
374
|
const policyPath = path.join(managedTmpDir, `evolcore-gemini-permission-${crypto.randomUUID()}.toml`);
|
|
312
|
-
fs.writeFileSync(policyPath, buildGeminiAdminPolicy(permissionProfile), { mode: 0o600, flag: 'wx' });
|
|
375
|
+
fs.writeFileSync(policyPath, buildGeminiAdminPolicy(permissionProfile, bootstrapAgentMdPath), { mode: 0o600, flag: 'wx' });
|
|
313
376
|
tempFiles.push(policyPath);
|
|
314
377
|
args.push(...buildGeminiPermissionArgs(permissionProfile, policyPath, !!sandboxProbe));
|
|
315
378
|
}
|
|
@@ -331,9 +394,12 @@ export class GeminiRunner {
|
|
|
331
394
|
args.push('--output-format', 'stream-json');
|
|
332
395
|
args.push('-m', callModel);
|
|
333
396
|
// Permission mode
|
|
334
|
-
if (permissionProfile.degradedFrom) {
|
|
397
|
+
if (permissionProfile.degradedFrom && !bootstrapAgentMdPath) {
|
|
335
398
|
logger.warn(`[GeminiRunner] permission mode ${permissionProfile.degradedFrom} is unavailable in headless mode; degraded to readonly`);
|
|
336
399
|
}
|
|
400
|
+
else if (bootstrapAgentMdPath) {
|
|
401
|
+
logger.info('[GeminiRunner] using bootstrap-only tool policy for the declared agent.md');
|
|
402
|
+
}
|
|
337
403
|
// Resume session
|
|
338
404
|
if (geminiSessionId) {
|
|
339
405
|
args.push('-r', geminiSessionId);
|
|
@@ -342,7 +408,12 @@ export class GeminiRunner {
|
|
|
342
408
|
const env = this.buildAgentEnv(sessionId, runtimeEnv);
|
|
343
409
|
const sandboxedCommand = buildBubblewrapCommand(this.resolved.cliPath, args, {
|
|
344
410
|
projectPath,
|
|
345
|
-
writablePaths: [
|
|
411
|
+
writablePaths: [
|
|
412
|
+
geminiHome,
|
|
413
|
+
...(runtimeDir ? [runtimeDir] : []),
|
|
414
|
+
...(runtimeLockDir ? [runtimeLockDir] : []),
|
|
415
|
+
...(bootstrapAgentMdPath ? [bootstrapAgentMdPath] : []),
|
|
416
|
+
],
|
|
346
417
|
readonlyPaths: tempFiles,
|
|
347
418
|
});
|
|
348
419
|
const child = spawn(sandboxedCommand?.command ?? this.resolved.cliPath, sandboxedCommand?.args ?? args, {
|
package/dist/aun/aid/store.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { AUN_KEYSTORE_ENCRYPTION_SEED } from './encryption-seed-policy.js';
|
|
4
|
+
// fastaun uses maxPages=1 for the background tail -> forward handoff. That is
|
|
5
|
+
// useful as a latency guard, but it can leave a group backlog when no later
|
|
6
|
+
// push arrives to trigger the next page. Bound the daemon recovery pull to ten
|
|
7
|
+
// pages so a large backlog is drained without monopolizing the sync loop.
|
|
8
|
+
const FULL_GROUP_PULL_MAX_PAGES = 10;
|
|
9
|
+
const GROUP_PULL_PAGINATION_PATCH = Symbol('evolcore.groupPullPaginationPatch');
|
|
4
10
|
/**
|
|
5
11
|
* Slot 命名约定(基于 fastaun 0.4.3 隔离键语义)。
|
|
6
12
|
*
|
|
@@ -68,6 +74,36 @@ export async function loadClient(store, aid) {
|
|
|
68
74
|
const { AUNClient } = await import('@agentunion/fastaun');
|
|
69
75
|
return new AUNClient(loadAid(store, aid));
|
|
70
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Enable bounded multi-page group pulls for the long-lived daemon client.
|
|
79
|
+
*
|
|
80
|
+
* The SDK's background realtime recovery path intentionally passes
|
|
81
|
+
* `maxPages=1`; for a daemon this can strand a backlog after a tail sync.
|
|
82
|
+
* This adapter only expands that sentinel value and leaves all other pull
|
|
83
|
+
* options unchanged. It is isolated to the client that explicitly opts in.
|
|
84
|
+
*/
|
|
85
|
+
export function enableFullGroupPullPagination(client) {
|
|
86
|
+
const target = client;
|
|
87
|
+
if (target[GROUP_PULL_PAGINATION_PATCH])
|
|
88
|
+
return false;
|
|
89
|
+
const pull = target._pullGroupV2;
|
|
90
|
+
if (typeof pull !== 'function')
|
|
91
|
+
return false;
|
|
92
|
+
const wrappedPull = function (groupId, afterSeq, limit, opts) {
|
|
93
|
+
const expandedOpts = opts?.maxPages === 1
|
|
94
|
+
? { ...opts, maxPages: FULL_GROUP_PULL_MAX_PAGES }
|
|
95
|
+
: opts;
|
|
96
|
+
return pull.call(this, groupId, afterSeq, limit, expandedOpts);
|
|
97
|
+
};
|
|
98
|
+
Object.defineProperty(target, GROUP_PULL_PAGINATION_PATCH, {
|
|
99
|
+
configurable: false,
|
|
100
|
+
enumerable: false,
|
|
101
|
+
value: true,
|
|
102
|
+
writable: false,
|
|
103
|
+
});
|
|
104
|
+
target._pullGroupV2 = wrappedPull;
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
71
107
|
/** 加载本地 AID 值对象(用于离线签名/验签,无需连接)。load 失败抛 AidLoadError。 */
|
|
72
108
|
export function loadAid(store, aid) {
|
|
73
109
|
const r = store.load(aid);
|
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,7 +4,7 @@ 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';
|
|
@@ -14,22 +14,34 @@ import { normalizeAunMentionEntries } from './mention-schema.js';
|
|
|
14
14
|
const DAEMON_SEND_TIMEOUT_GRACE_MS = 10_000;
|
|
15
15
|
const DEFAULT_IDLE_TIMEOUT_MS = 120_000;
|
|
16
16
|
const MAX_TIMER_MS = 2_147_483_647;
|
|
17
|
+
/**
|
|
18
|
+
* Read process settings through the config facade. A delegated CLI can run
|
|
19
|
+
* inside a sandbox where daemon.json is intentionally unreadable; sending a
|
|
20
|
+
* message must still use the protocol defaults instead of surfacing EACCES.
|
|
21
|
+
*/
|
|
22
|
+
function readDaemonConfigForSend() {
|
|
23
|
+
try {
|
|
24
|
+
return loadDaemonConfig();
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
17
30
|
export function daemonTaskSendIpcTimeoutMs(file = false) {
|
|
18
31
|
let idleTimeoutMs = DEFAULT_IDLE_TIMEOUT_MS;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
32
|
+
// Use the ConfigStore read path so managed tasks never open daemon.json
|
|
33
|
+
// directly. The daemon-owned config reader handles missing, invalid, and
|
|
34
|
+
// protected files without leaking a filesystem access error into IMRenderer.
|
|
35
|
+
const seconds = readDaemonConfigForSend().idleMonitor?.timeout;
|
|
36
|
+
if (typeof seconds === 'number' && Number.isFinite(seconds) && seconds > 0) {
|
|
37
|
+
idleTimeoutMs = seconds * 1000;
|
|
25
38
|
}
|
|
26
|
-
catch { }
|
|
27
39
|
return Math.min(MAX_TIMER_MS, Math.max(file ? 120_000 : 5_000, idleTimeoutMs + DAEMON_SEND_TIMEOUT_GRACE_MS));
|
|
28
40
|
}
|
|
29
41
|
export function resolveAunMessageEncrypt(explicit) {
|
|
30
42
|
if (typeof explicit === 'boolean')
|
|
31
43
|
return explicit;
|
|
32
|
-
return
|
|
44
|
+
return readDaemonConfigForSend().aun?.defaultEncrypt ?? true;
|
|
33
45
|
}
|
|
34
46
|
function buildSimplePayload(body) {
|
|
35
47
|
switch (body.mode) {
|
|
@@ -207,6 +219,7 @@ export async function msgSend(args) {
|
|
|
207
219
|
let payload;
|
|
208
220
|
if (args.body.mode !== 'file') {
|
|
209
221
|
payload = buildSimplePayload(args.body);
|
|
222
|
+
normalizeCodexTextPayload(payload, runtimeContext);
|
|
210
223
|
if (Object.prototype.hasOwnProperty.call(payload, 'mentions')) {
|
|
211
224
|
payload.mentions = normalizeAunMentionEntries(payload.mentions);
|
|
212
225
|
}
|
|
@@ -263,6 +276,7 @@ export async function msgSend(args) {
|
|
|
263
276
|
// 3. 构建 payload
|
|
264
277
|
if (!payload) {
|
|
265
278
|
payload = buildSimplePayload(args.body);
|
|
279
|
+
normalizeCodexTextPayload(payload, runtimeContext);
|
|
266
280
|
}
|
|
267
281
|
// 4. 写入 payload.chatmode
|
|
268
282
|
payload.chatmode = chatmode;
|