evolcore 0.0.20 → 0.0.21
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 +22 -0
- package/README.md +58 -9
- package/dist/agents/baseagent.js +10 -6
- package/dist/agents/claude-runner.js +379 -108
- package/dist/agents/codex-app-server-client.js +10 -2
- package/dist/agents/codex-runner.js +402 -135
- package/dist/agents/ecagent-runner.js +171 -61
- package/dist/agents/gemini-runner.js +130 -30
- package/dist/agents/request-identity.js +25 -0
- package/dist/agents/runner-types.js +19 -0
- package/dist/aun/aid/agentmd.js +59 -2
- package/dist/aun/aid/identity.js +4 -1
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/msg/group.js +72 -6
- package/dist/aun/msg/history.js +213 -36
- package/dist/aun/msg/managed-operation.js +58 -9
- package/dist/aun/msg/p2p.js +5 -0
- package/dist/aun/outbox.js +182 -80
- package/dist/aun/service-proxy.js +43 -25
- package/dist/channels/aun.js +409 -88
- package/dist/channels/daemon.js +6 -1
- package/dist/cli/agent-command.js +4 -3
- package/dist/cli/agent.js +66 -56
- package/dist/cli/aun-commands.js +177 -42
- package/dist/cli/command-log.js +10 -11
- package/dist/cli/contact.js +1 -0
- package/dist/cli/daemon-commands.js +69 -115
- package/dist/cli/init.js +27 -15
- package/dist/cli/task-context.js +46 -0
- package/dist/cli/trigger-command.js +1 -1
- package/dist/cli/watch-logs.js +10 -3
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-field-policy.js +16 -5
- package/dist/config/config-manager.js +135 -17
- package/dist/config/contact-operation-service.js +32 -1
- package/dist/config/contact-request-service.js +44 -0
- package/dist/config/daemon-services.js +186 -0
- package/dist/config/gateway-config.js +20 -9
- package/dist/config/role-service.js +54 -3
- package/dist/config/schema-migration.js +550 -0
- package/dist/config-store.js +151 -9
- package/dist/core/agent-application-service.js +279 -0
- package/dist/core/audit/log-integrity.js +102 -0
- package/dist/core/auth/agent-delegation.js +31 -1
- package/dist/core/auth/auth-gateway.js +33 -4
- package/dist/core/auth/authorization-audit.js +150 -2
- package/dist/core/auth/operation-authorizer.js +41 -1
- package/dist/core/auth/operation-catalog.js +9 -1
- package/dist/core/bootstrap-service.js +6 -2
- package/dist/core/causation/aun-association.js +7 -4
- package/dist/core/command/agent-control.js +56 -16
- package/dist/core/command/command-handler.js +290 -44
- package/dist/core/command/connect-menu.js +3 -4
- package/dist/core/command/group-menu.js +5 -7
- package/dist/core/command/menu-handler.js +279 -80
- package/dist/core/command/role-menu.js +21 -11
- package/dist/core/command/slash-gate.js +85 -18
- package/dist/core/command/slash-handler.js +350 -32
- package/dist/core/event-catalog.js +5 -0
- package/dist/core/evolagent.js +4 -0
- package/dist/core/handoff/dispatcher.js +4 -0
- package/dist/core/handoff/runtime.js +10 -0
- package/dist/core/handoff/store.js +32 -9
- package/dist/core/inference/text-inference.js +7 -15
- package/dist/core/message/im-renderer.js +83 -84
- package/dist/core/message/{inbound-admission.js → message-admission.js} +51 -1
- package/dist/core/message/message-bridge.js +124 -10
- package/dist/core/message/message-log.js +14 -7
- package/dist/core/message/message-queue.js +206 -16
- package/dist/core/message/message-utils.js +12 -5
- package/dist/core/message/response-engine.js +486 -68
- package/dist/core/message/send-receipt.js +1 -0
- package/dist/core/message/stream-debouncer.js +9 -2
- package/dist/core/model/model-catalog.js +23 -15
- package/dist/core/model/model-diagnostics.js +28 -10
- package/dist/core/permission/approval-gateway.js +180 -6
- package/dist/core/permission/ec-command-parser.js +410 -54
- package/dist/core/permission/mode.js +18 -3
- package/dist/core/{protected-paths.js → permission/protected-paths.js} +17 -5
- package/dist/core/permission/readonly-shell-query.js +263 -9
- package/dist/core/permission/sandbox-runtime.js +159 -1
- package/dist/core/permission/tool-policy.js +575 -21
- package/dist/core/session/session-fs-store.js +154 -5
- package/dist/core/session/session-manager.js +299 -30
- package/dist/core/session/session-renew.js +19 -12
- package/dist/core/session/session-turn-coordinator.js +11 -4
- package/dist/eck/kit-renderer.js +1 -1
- package/dist/index.js +253 -46
- package/dist/ipc.js +374 -24
- package/dist/paths.js +64 -7
- package/dist/response-system/context-builder.js +1 -7
- package/dist/trigger/anomaly-store.js +1 -0
- package/dist/trigger/feedback.js +56 -5
- package/dist/trigger/history.js +79 -4
- package/dist/trigger/legacy-session-history.js +2 -2
- package/dist/trigger/parser.js +3 -2
- package/dist/trigger/validation.js +6 -1
- package/dist/utils/atomic-write.js +27 -0
- package/dist/utils/ecweb-utils.js +16 -2
- package/dist/utils/error-utils.js +4 -1
- package/dist/utils/logger.js +21 -2
- package/dist/utils/process-tree-stats.js +24 -4
- package/dist/utils/process-tree-worker.js +31 -0
- package/dist/utils/project-path.js +1 -2
- package/kits/docs/INDEX.md +1 -1
- package/kits/docs/evolcore/INDEX.md +1 -1
- package/kits/docs/evolcore/contact.md +7 -1
- package/kits/docs/evolcore/msg.md +16 -0
- package/kits/schemas/_meta.json +4 -2
- package/kits/schemas/agent-config.schema.11.json +13 -0
- package/kits/schemas/daemon.schema.5.json +0 -1
- package/kits/schemas/daemon.schema.6.json +131 -0
- package/kits/schemas/defaults.schema.5.json +15 -3
- package/kits/schemas/migrations/README.md +3 -1
- package/kits/schemas/relation-config.schema.8.json +13 -0
- package/kits/schemas/role-config.schema.1.json +1 -2
- package/kits/schemas/single-session.schema.3.json +32 -0
- package/kits/templates/roles/admin.json +1 -0
- package/kits/templates/roles/member.json +1 -0
- package/kits/templates/roles/visitor.json +1 -0
- package/package.json +6 -3
- package/skills/eclink/SKILL.md +2 -0
- package/dist/config/aun-gateway-config.js +0 -2
|
@@ -18,7 +18,7 @@ import { tryUpgrade, tryUpgradeGlobalPkg, resolveGlobalPkg } from '../utils/npm-
|
|
|
18
18
|
import { ecwebVersionRequirementError, fetchEcwebPairCode, resolveEcwebLaunchCommand } from '../utils/ecweb-utils.js';
|
|
19
19
|
import { isHostChinese } from '../utils/locale.js';
|
|
20
20
|
import { scanInstances, cleanupInstances, findOrphanProcesses, killOrphans } from '../utils/instance-registry.js';
|
|
21
|
-
import { filterLogFiles, deriveLogTypes, computePreChecked, validateLogTypes, shortLogName as shortLogNameLocal } from './watch-logs.js';
|
|
21
|
+
import { filterLogFiles, deriveLogTypes, computePreChecked, validateLogTypes, shortLogName as shortLogNameLocal, isWatchLogFile } from './watch-logs.js';
|
|
22
22
|
import { displaySessionTitle } from '../core/session/session-title.js';
|
|
23
23
|
import { isNonAgentSessionChannel } from '../core/system-channels.js';
|
|
24
24
|
import { printCodeStats } from './code-stats.js';
|
|
@@ -29,6 +29,7 @@ import { rotateStdoutLog } from '../utils/log-writer.js';
|
|
|
29
29
|
import { inspectDataMigrationRequirement } from '../core/data-migration.js';
|
|
30
30
|
import { isInitCancelledError, printInitCancelHint } from './init-cancel.js';
|
|
31
31
|
import { listCodexAppServerProcesses, unregisterCodexAppServerProcess } from '../utils/codex-app-server-registry.js';
|
|
32
|
+
import { DEFAULT_ECWEB_PORT, ecwebService, setEcwebEnabled } from '../config/daemon-services.js';
|
|
32
33
|
const execFileAsync = promisify(execFile);
|
|
33
34
|
async function runAutomaticInit(action) {
|
|
34
35
|
try {
|
|
@@ -146,11 +147,6 @@ function buildDaemonEnv(p, opts) {
|
|
|
146
147
|
...(opts.bindBootstrap ? { EVOLCORE_BIND_BOOTSTRAP: '1' } : {}),
|
|
147
148
|
};
|
|
148
149
|
}
|
|
149
|
-
export function serviceProxyNeedsEcweb(cfg) {
|
|
150
|
-
if (!cfg.serviceProxy?.enabled)
|
|
151
|
-
return false;
|
|
152
|
-
return (cfg.serviceProxy.services ?? []).some(s => s.enabled !== false && s.source === 'ecweb');
|
|
153
|
-
}
|
|
154
150
|
function latestSourceMtime(dir) {
|
|
155
151
|
let latest = 0;
|
|
156
152
|
try {
|
|
@@ -437,8 +433,6 @@ export async function cmdStart(opts = {}) {
|
|
|
437
433
|
console.log(` Rotated: stdout.log -> ${path.basename(stdoutRotation.rotatedPath)}`);
|
|
438
434
|
}
|
|
439
435
|
cleanEnv();
|
|
440
|
-
// 在启动前确保 serviceProxy 配置完整(如果 ecweb 启用但 serviceProxy 未配置)
|
|
441
|
-
ensureServiceProxyConfigBeforeStart(p);
|
|
442
436
|
// 删除旧的 ready signal
|
|
443
437
|
try {
|
|
444
438
|
fs.unlinkSync(p.readySignal);
|
|
@@ -567,7 +561,7 @@ export async function cmdStart(opts = {}) {
|
|
|
567
561
|
}
|
|
568
562
|
console.log('');
|
|
569
563
|
// 代码统计仅在开发环境显示(EVOLCORE_HOME 指向包目录)
|
|
570
|
-
if (resolveRoot() === getPackageRoot()) {
|
|
564
|
+
if (path.normalize(resolveRoot()) === path.normalize(getPackageRoot())) {
|
|
571
565
|
printCodeStats(getPackageRoot(), p.logs);
|
|
572
566
|
}
|
|
573
567
|
console.log(`⏱ done in ${((Date.now() - cmdStartedAt) / 1000).toFixed(1)}s`);
|
|
@@ -808,7 +802,7 @@ export async function cmdRestart(opts = {}) {
|
|
|
808
802
|
console.log('🔄 Restart monitor started, waiting for service to come back online...');
|
|
809
803
|
await sleep(2000);
|
|
810
804
|
// 代码统计(开发环境)
|
|
811
|
-
if (resolveRoot() === getPackageRoot()) {
|
|
805
|
+
if (path.normalize(resolveRoot()) === path.normalize(getPackageRoot())) {
|
|
812
806
|
console.log('');
|
|
813
807
|
printCodeStats(getPackageRoot(), resolvePaths().logs);
|
|
814
808
|
}
|
|
@@ -975,7 +969,17 @@ export async function cmdStatus() {
|
|
|
975
969
|
const p = resolvePaths();
|
|
976
970
|
const status = scanInstances();
|
|
977
971
|
const aliveMains = status.mains.filter(m => m.alive);
|
|
978
|
-
|
|
972
|
+
// Start all read-only daemon queries together. On a busy Windows host each
|
|
973
|
+
// query can otherwise wait behind the same IPC/event-loop delay and make the
|
|
974
|
+
// total `ec status` time the sum of several individual timeouts.
|
|
975
|
+
const pingPromise = probeDaemon(p.socket).catch(() => null);
|
|
976
|
+
const statusPromise = ipcQuery(p.socket, { type: 'status' }).catch(() => null);
|
|
977
|
+
const aidsPromise = ipcQuery(p.socket, { type: 'aun-aids' }).catch(() => null);
|
|
978
|
+
const agentsPromise = ipcQuery(p.socket, { type: 'evolagent.list' }).catch(() => null);
|
|
979
|
+
// Only the ping is needed to decide whether to show the live-daemon
|
|
980
|
+
// sections. The remaining responses continue in parallel while local
|
|
981
|
+
// process/session information is rendered.
|
|
982
|
+
const ping = await pingPromise;
|
|
979
983
|
const pid = ping?.pid ?? (aliveMains.length > 0 ? aliveMains[0].record.pid : null);
|
|
980
984
|
const processVisible = !!pid && aliveMains.some(entry => entry.record.pid === pid);
|
|
981
985
|
if (aliveMains.length > 1) {
|
|
@@ -1131,42 +1135,47 @@ export async function cmdStatus() {
|
|
|
1131
1135
|
}
|
|
1132
1136
|
// Channel status. A running daemon is authoritative and avoids reading
|
|
1133
1137
|
// protected config files from the CLI's potentially sandboxed process.
|
|
1138
|
+
let agentsResponse = null;
|
|
1134
1139
|
if (pid) {
|
|
1135
1140
|
console.log('');
|
|
1136
|
-
const
|
|
1137
|
-
|
|
1141
|
+
const [statusResponse, aidsResponse, agentResponse] = await Promise.all([
|
|
1142
|
+
statusPromise,
|
|
1143
|
+
aidsPromise,
|
|
1144
|
+
agentsPromise,
|
|
1145
|
+
]);
|
|
1146
|
+
agentsResponse = agentResponse;
|
|
1147
|
+
if (statusResponse) {
|
|
1138
1148
|
// 🔑 AUN AIDs 表格(详细 AUN 实例状态)
|
|
1139
1149
|
try {
|
|
1140
|
-
|
|
1141
|
-
if (aidsResp?.ok && aidsResp.aids?.length > 0) {
|
|
1150
|
+
if (aidsResponse?.ok && aidsResponse.aids?.length > 0) {
|
|
1142
1151
|
console.log('🔑 AUN AIDs:');
|
|
1143
|
-
renderAunAidsTable(
|
|
1152
|
+
renderAunAidsTable(aidsResponse.aids);
|
|
1144
1153
|
}
|
|
1145
1154
|
}
|
|
1146
1155
|
catch { /* ignore */ }
|
|
1147
1156
|
// 控制 AID(daemon 进程身份)状态
|
|
1148
|
-
if (
|
|
1149
|
-
const state =
|
|
1150
|
-
console.log(`control: ${
|
|
1157
|
+
if (statusResponse.controlAid) {
|
|
1158
|
+
const state = statusResponse.controlAid.connected ? 'connected' : 'disconnected';
|
|
1159
|
+
console.log(`control: ${statusResponse.controlAid.aid} [${state}]`);
|
|
1151
1160
|
}
|
|
1152
1161
|
else {
|
|
1153
1162
|
console.log('control: not configured');
|
|
1154
1163
|
}
|
|
1155
|
-
if (
|
|
1164
|
+
if (statusResponse.stats) {
|
|
1156
1165
|
console.log('');
|
|
1157
1166
|
console.log('📊 Last hour:');
|
|
1158
|
-
console.log(` Messages: ${
|
|
1159
|
-
if (
|
|
1160
|
-
console.log(` Errors: ${
|
|
1161
|
-
if (
|
|
1162
|
-
console.log(` Avg response: ${(
|
|
1167
|
+
console.log(` Messages: ${statusResponse.stats.received} received, ${statusResponse.stats.completed} completed`);
|
|
1168
|
+
if (statusResponse.stats.errors > 0)
|
|
1169
|
+
console.log(` Errors: ${statusResponse.stats.errors}`);
|
|
1170
|
+
if (statusResponse.stats.completed > 0)
|
|
1171
|
+
console.log(` Avg response: ${(statusResponse.stats.avgResponseMs / 1000).toFixed(1)}s`);
|
|
1163
1172
|
}
|
|
1164
1173
|
}
|
|
1165
1174
|
else {
|
|
1166
1175
|
// IPC unreachable but PID exists — fall back to local config if readable.
|
|
1167
1176
|
const config = readStatusDefaultsConfig(p.defaultsConfig);
|
|
1168
1177
|
if (config) {
|
|
1169
|
-
console.log('🔌 Channels (
|
|
1178
|
+
console.log('🔌 Channels (live status unavailable; configuration is stale):');
|
|
1170
1179
|
showConfigChannels(config);
|
|
1171
1180
|
}
|
|
1172
1181
|
}
|
|
@@ -1182,7 +1191,7 @@ export async function cmdStatus() {
|
|
|
1182
1191
|
// EvolAgent summary (via IPC, only when running)
|
|
1183
1192
|
if (pid) {
|
|
1184
1193
|
try {
|
|
1185
|
-
const agentResult =
|
|
1194
|
+
const agentResult = agentsResponse;
|
|
1186
1195
|
if (agentResult?.ok && agentResult.agents?.length > 0) {
|
|
1187
1196
|
const agents = agentResult.agents;
|
|
1188
1197
|
if (agents.length > 0) {
|
|
@@ -1196,6 +1205,22 @@ export async function cmdStatus() {
|
|
|
1196
1205
|
}
|
|
1197
1206
|
}
|
|
1198
1207
|
}
|
|
1208
|
+
else {
|
|
1209
|
+
const { EvolAgentRegistry } = await import('../core/evolagent-registry.js');
|
|
1210
|
+
const diskRegistry = new EvolAgentRegistry(p.agentsDir);
|
|
1211
|
+
diskRegistry.loadAll();
|
|
1212
|
+
const agents = diskRegistry.list();
|
|
1213
|
+
if (agents.length > 0) {
|
|
1214
|
+
console.log('');
|
|
1215
|
+
console.log('🤖 EvolAgents (live status unavailable; stale configuration):');
|
|
1216
|
+
for (const a of agents) {
|
|
1217
|
+
const diskStatus = a.status === 'disabled' || a.status === 'error' ? a.status : 'unknown';
|
|
1218
|
+
const channels = summarizeChannelFingerprints(a.channels || []);
|
|
1219
|
+
const shortName = a.name.replace(/\.agentid\.pub$/, '');
|
|
1220
|
+
console.log(` ? ${shortName.padEnd(20)} ${`${diskStatus} (stale)`.padEnd(18)} ${channels}`);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1199
1224
|
}
|
|
1200
1225
|
catch {
|
|
1201
1226
|
// IPC query for agents failed — skip section
|
|
@@ -1789,10 +1814,10 @@ async function cmdWatchLogsFlow() {
|
|
|
1789
1814
|
console.log(`❌ Log directory not found: ${p.logs}`);
|
|
1790
1815
|
process.exit(1);
|
|
1791
1816
|
}
|
|
1792
|
-
const files = fs.readdirSync(p.logs).filter(
|
|
1817
|
+
const files = fs.readdirSync(p.logs).filter(isWatchLogFile);
|
|
1793
1818
|
const types = deriveLogTypes(files);
|
|
1794
1819
|
if (types.length === 0) {
|
|
1795
|
-
console.log(`⚠ ${p.logs}
|
|
1820
|
+
console.log(`⚠ ${p.logs} 下暂无可监听日志文件`);
|
|
1796
1821
|
return;
|
|
1797
1822
|
}
|
|
1798
1823
|
const fileCount = new Map();
|
|
@@ -1863,7 +1888,7 @@ function cmdWatch(filterTypes) {
|
|
|
1863
1888
|
return c;
|
|
1864
1889
|
};
|
|
1865
1890
|
const listLogs = () => {
|
|
1866
|
-
const all = fs.readdirSync(p.logs).filter(
|
|
1891
|
+
const all = fs.readdirSync(p.logs).filter(isWatchLogFile).map(f => path.join(p.logs, f));
|
|
1867
1892
|
return filterLogFiles(all, filterTypes);
|
|
1868
1893
|
};
|
|
1869
1894
|
const shortName = shortLogNameLocal;
|
|
@@ -1885,7 +1910,7 @@ function cmdWatch(filterTypes) {
|
|
|
1885
1910
|
const content = formatWatchContent(line);
|
|
1886
1911
|
return `${timeStr} ${paddedName} ${content}`;
|
|
1887
1912
|
};
|
|
1888
|
-
console.log(`🔭 Watching ${p.logs}
|
|
1913
|
+
console.log(`🔭 Watching ${p.logs}/* log files (ESC to stop)\n`);
|
|
1889
1914
|
// 显示当前实例信息和 AID 状态
|
|
1890
1915
|
const instStatus = scanInstances();
|
|
1891
1916
|
const aliveMainEntries = instStatus.mains.filter(m => m.alive);
|
|
@@ -2508,9 +2533,9 @@ async function waitForExistingEcweb(p, timeoutMs = 3_000) {
|
|
|
2508
2533
|
async function printEcwebStatus(p) {
|
|
2509
2534
|
try {
|
|
2510
2535
|
const cfg = loadDaemonConfig();
|
|
2511
|
-
if (cfg.
|
|
2536
|
+
if (ecwebService(cfg.services)?.enabled === false) {
|
|
2512
2537
|
console.log('');
|
|
2513
|
-
console.log('🔭 ECWeb: 已禁用 (daemon.json → ecweb.enabled: false)');
|
|
2538
|
+
console.log('🔭 ECWeb: 已禁用 (daemon.json → services[name=ecweb].enabled: false)');
|
|
2514
2539
|
return;
|
|
2515
2540
|
}
|
|
2516
2541
|
}
|
|
@@ -2608,7 +2633,7 @@ export function stopCodexAppServerOrphans() {
|
|
|
2608
2633
|
/** 若 ecweb 在运行则杀掉并确认 pid/端口都已释放。 */
|
|
2609
2634
|
async function stopEcwebIfRunning(p) {
|
|
2610
2635
|
const alive = findAliveEcweb(p);
|
|
2611
|
-
const port = loadDaemonConfig().
|
|
2636
|
+
const port = ecwebService(loadDaemonConfig().services)?.port ?? DEFAULT_ECWEB_PORT;
|
|
2612
2637
|
const pids = new Set([
|
|
2613
2638
|
...(alive ? [alive.pid] : []),
|
|
2614
2639
|
...platform.findProcessByPort(port),
|
|
@@ -2634,78 +2659,6 @@ async function waitForPortRelease(port, timeoutMs) {
|
|
|
2634
2659
|
}
|
|
2635
2660
|
return platform.findProcessByPort(port).length === 0;
|
|
2636
2661
|
}
|
|
2637
|
-
/**
|
|
2638
|
-
* 后台 detached 启动 ecweb;若已运行则先停再启(确保加载最新代码)。
|
|
2639
|
-
* 启动后轮询端口确认 HTTP 服务真正就绪,打印明确的成功/失败结论(而非模糊的「已在后台启动」状态描述)。
|
|
2640
|
-
* 返回 true=本次确实启动成功,false=未启用/未安装/启动失败。
|
|
2641
|
-
*/
|
|
2642
|
-
/**
|
|
2643
|
-
* 启动前检查并补全 serviceProxy 配置(如果 ecweb 启用但 serviceProxy 未配置)
|
|
2644
|
-
*/
|
|
2645
|
-
function ensureServiceProxyConfigBeforeStart(p) {
|
|
2646
|
-
const cfg = loadDaemonConfig();
|
|
2647
|
-
// 仅在 ecweb 启用 + 控制 AID 已配置 + serviceProxy 未配置 ecweb 时自动添加
|
|
2648
|
-
if (!cfg.ecweb?.enabled || !cfg.aid)
|
|
2649
|
-
return;
|
|
2650
|
-
const hasEcwebService = (cfg.serviceProxy?.services ?? []).some(s => s.name === 'ecweb' || s.source === 'ecweb');
|
|
2651
|
-
if (!hasEcwebService) {
|
|
2652
|
-
const updatedCfg = {
|
|
2653
|
-
...cfg,
|
|
2654
|
-
serviceProxy: {
|
|
2655
|
-
enabled: true,
|
|
2656
|
-
...(cfg.serviceProxy ?? {}),
|
|
2657
|
-
services: [
|
|
2658
|
-
...(cfg.serviceProxy?.services ?? []),
|
|
2659
|
-
{
|
|
2660
|
-
name: 'ecweb',
|
|
2661
|
-
source: 'ecweb',
|
|
2662
|
-
serviceType: 'http',
|
|
2663
|
-
visibility: 'public',
|
|
2664
|
-
enabled: true,
|
|
2665
|
-
metadata: {
|
|
2666
|
-
label: 'EvolCore Dashboard',
|
|
2667
|
-
},
|
|
2668
|
-
},
|
|
2669
|
-
],
|
|
2670
|
-
},
|
|
2671
|
-
};
|
|
2672
|
-
saveDaemonConfig(updatedCfg);
|
|
2673
|
-
console.log(`✓ 自动配置 Service Proxy: https://${cfg.aid}/proxy/ecweb/`);
|
|
2674
|
-
}
|
|
2675
|
-
}
|
|
2676
|
-
/**
|
|
2677
|
-
* 确保 serviceProxy 配置包含 ecweb 服务(ECWeb 启动成功后自动配置)
|
|
2678
|
-
*/
|
|
2679
|
-
function ensureServiceProxyConfig(cfg, port) {
|
|
2680
|
-
// 仅在控制 AID 已配置时自动添加 serviceProxy
|
|
2681
|
-
if (!cfg.aid)
|
|
2682
|
-
return;
|
|
2683
|
-
const hasEcwebService = (cfg.serviceProxy?.services ?? []).some(s => s.name === 'ecweb' || s.source === 'ecweb');
|
|
2684
|
-
if (!hasEcwebService) {
|
|
2685
|
-
const updatedCfg = {
|
|
2686
|
-
...cfg,
|
|
2687
|
-
serviceProxy: {
|
|
2688
|
-
enabled: true,
|
|
2689
|
-
...(cfg.serviceProxy ?? {}),
|
|
2690
|
-
services: [
|
|
2691
|
-
...(cfg.serviceProxy?.services ?? []),
|
|
2692
|
-
{
|
|
2693
|
-
name: 'ecweb',
|
|
2694
|
-
source: 'ecweb',
|
|
2695
|
-
serviceType: 'http',
|
|
2696
|
-
visibility: 'public',
|
|
2697
|
-
enabled: true,
|
|
2698
|
-
metadata: {
|
|
2699
|
-
label: 'EvolCore Dashboard',
|
|
2700
|
-
},
|
|
2701
|
-
},
|
|
2702
|
-
],
|
|
2703
|
-
},
|
|
2704
|
-
};
|
|
2705
|
-
saveDaemonConfig(updatedCfg);
|
|
2706
|
-
console.log(` 自动配置 Service Proxy: https://${cfg.aid}/proxy/ecweb/`);
|
|
2707
|
-
}
|
|
2708
|
-
}
|
|
2709
2662
|
/**
|
|
2710
2663
|
* Start the separately installed `ec-web` process for a configured runtime.
|
|
2711
2664
|
* Kept public for restart-monitor, which launches the daemon directly and
|
|
@@ -2713,7 +2666,8 @@ function ensureServiceProxyConfig(cfg, port) {
|
|
|
2713
2666
|
*/
|
|
2714
2667
|
export async function startEcwebIfEnabled(p, options = {}) {
|
|
2715
2668
|
const cfg = loadDaemonConfig();
|
|
2716
|
-
|
|
2669
|
+
const service = ecwebService(cfg.services);
|
|
2670
|
+
if (!service?.enabled)
|
|
2717
2671
|
return false;
|
|
2718
2672
|
// The daemon is the lifecycle owner. CLI callers normally only need to
|
|
2719
2673
|
// ensure ECWeb is available, so adopt a healthy process instead of killing
|
|
@@ -2750,7 +2704,7 @@ export async function startEcwebIfEnabled(p, options = {}) {
|
|
|
2750
2704
|
console.log(`❌ EC Web 旧进程未清理干净,取消启动: ${error instanceof Error ? error.message : String(error)}`);
|
|
2751
2705
|
return false;
|
|
2752
2706
|
}
|
|
2753
|
-
const port =
|
|
2707
|
+
const port = service.port ?? DEFAULT_ECWEB_PORT;
|
|
2754
2708
|
const args = ['--home', p.root, '--port', String(port)];
|
|
2755
2709
|
const launch = resolveEcwebLaunchCommand(args, { installedPkg });
|
|
2756
2710
|
if (!launch) {
|
|
@@ -2880,10 +2834,9 @@ async function cmdWatchWeb() {
|
|
|
2880
2834
|
}
|
|
2881
2835
|
// 2. 启动(后台)并同步配置。默认行为是替换旧实例,确保新配置/新版静态资源生效。
|
|
2882
2836
|
const cfg = loadDaemonConfig();
|
|
2883
|
-
const
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
ensureServiceProxyConfigBeforeStart(p);
|
|
2837
|
+
const currentService = ecwebService(cfg.services);
|
|
2838
|
+
const port = currentService?.port ?? DEFAULT_ECWEB_PORT;
|
|
2839
|
+
saveDaemonConfig({ ...cfg, services: setEcwebEnabled(cfg.services, true, port) });
|
|
2887
2840
|
const ok = await startEcwebIfEnabled(p, { forceRestart: true });
|
|
2888
2841
|
if (!ok)
|
|
2889
2842
|
process.exit(1); // 失败原因已由 startEcwebIfEnabled 打印
|
|
@@ -3011,8 +2964,9 @@ export async function cmdDiagnose() {
|
|
|
3011
2964
|
// 6. 检查 EC Web 端口与实例记录是否一致。
|
|
3012
2965
|
try {
|
|
3013
2966
|
const cfg = loadDaemonConfig();
|
|
3014
|
-
|
|
3015
|
-
|
|
2967
|
+
const service = ecwebService(cfg.services);
|
|
2968
|
+
if (service?.enabled) {
|
|
2969
|
+
const port = service.port ?? DEFAULT_ECWEB_PORT;
|
|
3016
2970
|
const portPids = platform.findProcessByPort(port);
|
|
3017
2971
|
const recordedPids = new Set(readEcwebInstanceEntries(p).map(entry => entry.record.pid));
|
|
3018
2972
|
const unregisteredPids = portPids.filter(pid => !recordedPids.has(pid));
|
|
@@ -3083,7 +3037,7 @@ export async function cmdWatchCommand(args) {
|
|
|
3083
3037
|
if (requested.length > 0) {
|
|
3084
3038
|
const p2 = resolvePaths();
|
|
3085
3039
|
const avail = fs.existsSync(p2.logs)
|
|
3086
|
-
? deriveLogTypes(fs.readdirSync(p2.logs).filter(
|
|
3040
|
+
? deriveLogTypes(fs.readdirSync(p2.logs).filter(isWatchLogFile))
|
|
3087
3041
|
: [];
|
|
3088
3042
|
const invalid = validateLogTypes(requested, avail);
|
|
3089
3043
|
if (invalid.length > 0) {
|
package/dist/cli/init.js
CHANGED
|
@@ -6,6 +6,7 @@ import { resolvePaths, ensureDataDirs } from '../paths.js';
|
|
|
6
6
|
import { commandExists } from '../utils/cross-platform.js';
|
|
7
7
|
import { scanInstances } from '../utils/instance-registry.js';
|
|
8
8
|
import { saveDefaultsSafe, loadAllAgents, loadDaemonConfig, saveDaemonConfig } from '../config-store.js';
|
|
9
|
+
import { ecwebService, setEcwebEnabled } from '../config/daemon-services.js';
|
|
9
10
|
import { generateControlAid, resolveControlAidDomain } from '../aun/aid/control-aid.js';
|
|
10
11
|
import { getCodexAppServerAvailability } from '../agents/codex-runner.js';
|
|
11
12
|
import { DEFAULT_ECAGENT_BASE_URL, resolveEcagentConfig } from '../agents/baseagent.js';
|
|
@@ -421,6 +422,12 @@ export async function cmdInit(options) {
|
|
|
421
422
|
const lower = normalized.toLowerCase();
|
|
422
423
|
return !lower.includes('your-') && !lower.includes('placeholder');
|
|
423
424
|
};
|
|
425
|
+
const configuredBaseUrl = [
|
|
426
|
+
configured?.baseUrl,
|
|
427
|
+
process.env.ECAGENT_BASE_URL,
|
|
428
|
+
DEFAULT_ECAGENT_BASE_URL,
|
|
429
|
+
].find(usable) || DEFAULT_ECAGENT_BASE_URL;
|
|
430
|
+
const baseUrl = (await ask(rl, `ecagent Base URL [${configuredBaseUrl}]: `)).trim() || configuredBaseUrl;
|
|
424
431
|
const configuredApiKey = usable(configured?.apiKey) ? configured.apiKey.trim() : undefined;
|
|
425
432
|
const environmentApiKeyName = !configuredApiKey
|
|
426
433
|
? (usable(process.env.ECAGENT_API_KEY)
|
|
@@ -435,12 +442,6 @@ export async function cmdInit(options) {
|
|
|
435
442
|
if (!apiKey)
|
|
436
443
|
console.log(' API Key 不能为空,请重新输入');
|
|
437
444
|
}
|
|
438
|
-
const configuredBaseUrl = [
|
|
439
|
-
configured?.baseUrl,
|
|
440
|
-
process.env.ECAGENT_BASE_URL,
|
|
441
|
-
DEFAULT_ECAGENT_BASE_URL,
|
|
442
|
-
].find(usable) || DEFAULT_ECAGENT_BASE_URL;
|
|
443
|
-
const baseUrl = (await ask(rl, `ecagent Base URL [${configuredBaseUrl}]: `)).trim() || configuredBaseUrl;
|
|
444
445
|
return { apiKey, baseUrl };
|
|
445
446
|
}
|
|
446
447
|
try {
|
|
@@ -618,25 +619,34 @@ export async function initTail(options = {}) {
|
|
|
618
619
|
}
|
|
619
620
|
async function handleEcwebPrompt(rl) {
|
|
620
621
|
const daemonConfigForEcweb = loadDaemonConfig();
|
|
621
|
-
if (daemonConfigForEcweb.
|
|
622
|
+
if (ecwebService(daemonConfigForEcweb.services)?.enabled === undefined) {
|
|
622
623
|
const ans = (await ask(rl, '\n是否在 ec start 时自动启动 ECWeb 控制台?[y/N] ')).trim().toLowerCase();
|
|
623
624
|
if (ans === 'y' || ans === 'yes') {
|
|
624
625
|
const installResult = await ensureEcwebInstalledForInit();
|
|
625
626
|
if (installResult.ok) {
|
|
626
627
|
const cfg = loadDaemonConfig();
|
|
627
|
-
saveDaemonConfig({
|
|
628
|
+
saveDaemonConfig({
|
|
629
|
+
...cfg,
|
|
630
|
+
services: setEcwebEnabled(cfg.services, true),
|
|
631
|
+
});
|
|
628
632
|
console.log(' ✓ 已启用 ECWeb(ec start 将自动在后台启动)');
|
|
629
633
|
console.log(' 提示:首次访问运行 ec watch web 查看配对码和 URL');
|
|
630
634
|
}
|
|
631
635
|
else {
|
|
632
636
|
const cfg = loadDaemonConfig();
|
|
633
|
-
saveDaemonConfig({
|
|
637
|
+
saveDaemonConfig({
|
|
638
|
+
...cfg,
|
|
639
|
+
services: setEcwebEnabled(cfg.services, false),
|
|
640
|
+
});
|
|
634
641
|
console.log(' 提示:安装完成后运行 ec start;如需立即打开控制台,运行 ec watch web');
|
|
635
642
|
}
|
|
636
643
|
}
|
|
637
644
|
else {
|
|
638
645
|
const cfg = loadDaemonConfig();
|
|
639
|
-
saveDaemonConfig({
|
|
646
|
+
saveDaemonConfig({
|
|
647
|
+
...cfg,
|
|
648
|
+
services: setEcwebEnabled(cfg.services, false),
|
|
649
|
+
});
|
|
640
650
|
console.log(' 已跳过(可日后运行 ec watch web 手动启动,或编辑 daemon.json)');
|
|
641
651
|
}
|
|
642
652
|
}
|
|
@@ -658,7 +668,7 @@ export async function initTail(options = {}) {
|
|
|
658
668
|
const finalCfg = loadDaemonConfig();
|
|
659
669
|
console.log(` 控制 AID: ${finalCfg.aid || '(未配置)'}`);
|
|
660
670
|
console.log(` 管理者: ${finalCfg.owners?.join(', ') || '(未配置)'}`);
|
|
661
|
-
console.log(` ECWeb 自启动: ${finalCfg.
|
|
671
|
+
console.log(` ECWeb 自启动: ${ecwebService(finalCfg.services)?.enabled ? '已启用' : '已禁用'}`);
|
|
662
672
|
const { agents: finalAgents } = loadAllAgents();
|
|
663
673
|
if (finalAgents.length === 0) {
|
|
664
674
|
console.log('\n📌 下一步:创建 agent');
|
|
@@ -826,11 +836,11 @@ export async function cmdInitNonInteractive(opts, dependencies = {}) {
|
|
|
826
836
|
const ensureEcweb = dependencies.ensureEcweb ?? (() => ensureEcwebInstalledForInit({ quiet: true }));
|
|
827
837
|
const installResult = await ensureEcweb();
|
|
828
838
|
if (!installResult.ok) {
|
|
829
|
-
if (existingCfg.
|
|
839
|
+
if (ecwebService(existingCfg.services)?.enabled === true) {
|
|
830
840
|
try {
|
|
831
841
|
saveDaemonConfig({
|
|
832
842
|
...existingCfg,
|
|
833
|
-
|
|
843
|
+
services: setEcwebEnabled(existingCfg.services, false),
|
|
834
844
|
});
|
|
835
845
|
}
|
|
836
846
|
catch (e) {
|
|
@@ -866,7 +876,9 @@ export async function cmdInitNonInteractive(opts, dependencies = {}) {
|
|
|
866
876
|
$schema_version: existingCfg.$schema_version ?? 1,
|
|
867
877
|
aid: controlAid,
|
|
868
878
|
owners: [ownerAid],
|
|
869
|
-
...(opts.ecweb === true
|
|
879
|
+
...(opts.ecweb === true
|
|
880
|
+
? { services: setEcwebEnabled(existingCfg.services, true) }
|
|
881
|
+
: {}),
|
|
870
882
|
};
|
|
871
883
|
saveDaemonConfig(next);
|
|
872
884
|
}
|
|
@@ -894,7 +906,7 @@ export async function cmdInitNonInteractive(opts, dependencies = {}) {
|
|
|
894
906
|
controlAid: controlAid,
|
|
895
907
|
ownerAid,
|
|
896
908
|
owners: [ownerAid],
|
|
897
|
-
ecwebEnabled: opts.ecweb === true || existingCfg.
|
|
909
|
+
ecwebEnabled: opts.ecweb === true || ecwebService(existingCfg.services)?.enabled === true,
|
|
898
910
|
baseagent: chosenBaseagent,
|
|
899
911
|
projectsDefaultPath: projectsDefaultPath ?? null,
|
|
900
912
|
defaultsPath: p.defaultsConfig,
|
package/dist/cli/task-context.js
CHANGED
|
@@ -32,6 +32,11 @@ function normalizeTaskRuntimeContext(value) {
|
|
|
32
32
|
peerName: optionalString(value.peerName),
|
|
33
33
|
peerType: optionalString(value.peerType),
|
|
34
34
|
peerRole: optionalString(value.peerRole),
|
|
35
|
+
processRole: optionalProcessRole(value.processRole),
|
|
36
|
+
dataScope: optionalDataScope(value.dataScope),
|
|
37
|
+
authorizedBy: optionalString(value.authorizedBy),
|
|
38
|
+
executionSource: value.executionSource === 'fullaccess-command' || value.executionSource === 'trigger'
|
|
39
|
+
? value.executionSource : undefined,
|
|
35
40
|
threadId: optionalString(value.threadId),
|
|
36
41
|
sessionRuntimeDir: optionalAbsolutePath(value.sessionRuntimeDir),
|
|
37
42
|
runtimeLockDir: optionalAbsolutePath(value.runtimeLockDir),
|
|
@@ -39,6 +44,13 @@ function normalizeTaskRuntimeContext(value) {
|
|
|
39
44
|
causation: normalizeCausation(value.causation),
|
|
40
45
|
};
|
|
41
46
|
}
|
|
47
|
+
function optionalProcessRole(value) {
|
|
48
|
+
return value === 'daemon-owner' || value === 'daemon-service' || value === 'fullaccess-run' || value === 'none'
|
|
49
|
+
? value : undefined;
|
|
50
|
+
}
|
|
51
|
+
function optionalDataScope(value) {
|
|
52
|
+
return value === 'relation' || value === 'agent' || value === 'daemon' ? value : undefined;
|
|
53
|
+
}
|
|
42
54
|
/**
|
|
43
55
|
* Codex's shell carrier can leave one JSON-style escape layer in a message
|
|
44
56
|
* argument (for example, the two characters `\\` and `n`). Decode only that
|
|
@@ -198,6 +210,40 @@ export function isManagedSessionRuntimeDir(directory, managedRoot) {
|
|
|
198
210
|
return false;
|
|
199
211
|
return isPrivateDirectory(root) && isPrivateDirectory(resolved);
|
|
200
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Resolve the session-owned temporary root for a managed child process.
|
|
215
|
+
*
|
|
216
|
+
* A direct runner call without task context is intentionally left alone for
|
|
217
|
+
* backwards-compatible tests and low-level integrations. Once the daemon has
|
|
218
|
+
* injected the task-context marker, however, falling back to the daemon's
|
|
219
|
+
* process-wide TMPDIR would break session isolation. Require the injected
|
|
220
|
+
* value to be present, identical to TMPDIR, and still owned by a trusted
|
|
221
|
+
* managed namespace before any runner-side helper writes a file.
|
|
222
|
+
*/
|
|
223
|
+
export function getManagedTaskTempDir(runtimeEnv) {
|
|
224
|
+
if (typeof runtimeEnv?.[TASK_RUNTIME_CONTEXT_ENV] !== 'string')
|
|
225
|
+
return undefined;
|
|
226
|
+
const configured = runtimeEnv[SESSION_RUNTIME_DIR_ENV]?.trim();
|
|
227
|
+
const inherited = runtimeEnv.TMPDIR?.trim();
|
|
228
|
+
const context = parseTaskRuntimeContext(runtimeEnv[TASK_RUNTIME_CONTEXT_ENV]);
|
|
229
|
+
const contextDir = context?.sessionRuntimeDir;
|
|
230
|
+
const processRoot = process.env.TMPDIR?.trim();
|
|
231
|
+
const trusted = configured
|
|
232
|
+
&& inherited
|
|
233
|
+
&& path.isAbsolute(configured)
|
|
234
|
+
&& path.isAbsolute(inherited)
|
|
235
|
+
&& path.resolve(configured) === path.resolve(inherited)
|
|
236
|
+
// The process root is shared by all sessions. It may contain a session
|
|
237
|
+
// directory, but must never itself become the task's TMPDIR capability.
|
|
238
|
+
&& (!processRoot || !path.isAbsolute(processRoot) || path.resolve(configured) !== path.resolve(processRoot))
|
|
239
|
+
&& contextDir
|
|
240
|
+
&& path.resolve(contextDir) === path.resolve(configured)
|
|
241
|
+
&& (isManagedSessionRuntimeDir(configured) || isRunnerOwnedSessionRuntimeDir(configured));
|
|
242
|
+
if (!trusted) {
|
|
243
|
+
throw new Error('managed session TMPDIR is unavailable or untrusted');
|
|
244
|
+
}
|
|
245
|
+
return path.resolve(configured);
|
|
246
|
+
}
|
|
201
247
|
/**
|
|
202
248
|
* Create a private runtime directory below the process-provided TMPDIR.
|
|
203
249
|
* There is deliberately no os.tmpdir() fallback: managed sessions must not
|
|
@@ -110,7 +110,7 @@ Create 参数模式支持:
|
|
|
110
110
|
--baseagent <name> (仅 --exec trigger-session)
|
|
111
111
|
--model <模型> --effort <low|medium|high|xhigh|max>
|
|
112
112
|
--max-runs <次数> --max-duration <时长: 30s|15m|2h|1d>
|
|
113
|
-
--permission <readonly|auto|request|bypass
|
|
113
|
+
--permission <readonly|auto|request|bypass|fullaccess>(省略则继承当前身份配置;fullaccess 仅 daemon-owner 且 feature flag 开启时可用)
|
|
114
114
|
--tz <时区> (仅 cron)
|
|
115
115
|
--target-channel <channelKey> --target-channel-id <ID> (AUN 可简写为 aun)
|
|
116
116
|
--target-chat-type <private|group> (AUN 目标必填;channelId 同时作为群聊 ID)
|
package/dist/cli/watch-logs.js
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
+
const WATCH_LOG_FILE = /\.(?:log|jsonl|out)(?:\..+)?$/;
|
|
3
|
+
/** Whether a file is a supported input for the interactive log watcher. */
|
|
4
|
+
export function isWatchLogFile(file) {
|
|
5
|
+
return WATCH_LOG_FILE.test(path.basename(file));
|
|
6
|
+
}
|
|
2
7
|
/** 去掉轮转后缀(按小时、按日及旧版带连字符日期)。入参可为文件名或绝对路径。 */
|
|
3
8
|
export function shortLogName(file) {
|
|
4
|
-
|
|
9
|
+
const baseName = path.basename(file);
|
|
10
|
+
const stem = baseName.replace(WATCH_LOG_FILE, '');
|
|
11
|
+
return stem
|
|
5
12
|
.replace(/-\d{8}(?:-\d{2})?$/, '') // -YYYYMMDD[-HH](按日/小时轮转)
|
|
6
13
|
.replace(/-\d{4}-\d{2}-\d{2}$/, ''); // -YYYY-MM-DD(按日轮转,如 ts-sdk)
|
|
7
14
|
}
|
|
8
|
-
/**
|
|
15
|
+
/** 从支持的日志文件名列表推导去重、字母序的类型列表。 */
|
|
9
16
|
export function deriveLogTypes(files) {
|
|
10
17
|
const set = new Set();
|
|
11
18
|
for (const f of files) {
|
|
12
|
-
if (!f
|
|
19
|
+
if (!isWatchLogFile(f))
|
|
13
20
|
continue;
|
|
14
21
|
set.add(shortLogName(f));
|
|
15
22
|
}
|
|
@@ -32,6 +32,7 @@ export function getBuiltinRolesConfig() {
|
|
|
32
32
|
export function getManagementCommandPermissions(role) {
|
|
33
33
|
return {
|
|
34
34
|
'contact.read': { allow: true, scopes: ['agent'] },
|
|
35
|
+
'contact.add': { allow: true, scopes: ['relation'], constraints: { ownPeerOnly: true, privateOnly: true } },
|
|
35
36
|
'contact.block': { allow: true, scopes: ['agent'], constraints: { requireAgentAdmin: true } },
|
|
36
37
|
'connect.write': { allow: true, scopes: ['agent'], constraints: { requireAgentAdmin: true } },
|
|
37
38
|
'connect.access.write': { allow: true, scopes: ['agent'], constraints: { requireAgentOwner: true } },
|
|
@@ -17,6 +17,8 @@ const BEHAVIOR_TOP_FIELDS = new Set([
|
|
|
17
17
|
const BASEAGENT_BEHAVIOR_FIELDS = new Set([
|
|
18
18
|
'model',
|
|
19
19
|
'effort',
|
|
20
|
+
'auxiliaryModel',
|
|
21
|
+
'auxiliaryEffort',
|
|
20
22
|
'reasoning',
|
|
21
23
|
'agentProgressSummaries',
|
|
22
24
|
'excludeDynamicSections',
|
|
@@ -25,7 +27,7 @@ const BASEAGENT_BEHAVIOR_FIELDS = new Set([
|
|
|
25
27
|
'mode',
|
|
26
28
|
'useVertex',
|
|
27
29
|
]);
|
|
28
|
-
const SUPPORTED_BASEAGENTS = new Set(['claude', 'codex', 'gemini', 'ecagent'
|
|
30
|
+
const SUPPORTED_BASEAGENTS = new Set(['claude', 'codex', 'gemini', 'ecagent']);
|
|
29
31
|
const SENSITIVE_TOP_FIELDS = new Set([
|
|
30
32
|
'$schema_version',
|
|
31
33
|
'aid',
|
|
@@ -75,10 +77,17 @@ export function resolveConfigFieldRule(fieldPath) {
|
|
|
75
77
|
const [, baseagent, leaf] = parts;
|
|
76
78
|
if (!SUPPORTED_BASEAGENTS.has(baseagent))
|
|
77
79
|
return { class: 'unknown' };
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
+
const normalizedLeaf = leaf === 'reasoning'
|
|
81
|
+
? 'effort'
|
|
82
|
+
: leaf === 'auxiliaryModel'
|
|
83
|
+
? 'model'
|
|
84
|
+
: leaf === 'auxiliaryEffort'
|
|
85
|
+
? 'effort'
|
|
86
|
+
: leaf;
|
|
87
|
+
const permissionKey = `baseagents.${baseagent}.${normalizedLeaf}`;
|
|
88
|
+
if ((leaf === 'model' || leaf === 'auxiliaryModel') && parts.length === 3)
|
|
80
89
|
return scalar(permissionKey, 'string');
|
|
81
|
-
if ((leaf === 'effort' || leaf === 'reasoning') && parts.length === 3)
|
|
90
|
+
if ((leaf === 'effort' || leaf === 'reasoning' || leaf === 'auxiliaryEffort') && parts.length === 3)
|
|
82
91
|
return scalar(permissionKey, 'effort');
|
|
83
92
|
if (baseagent === 'claude' && (leaf === 'agentProgressSummaries' || leaf === 'excludeDynamicSections') && parts.length === 3) {
|
|
84
93
|
return scalar(permissionKey, 'boolean');
|
|
@@ -199,7 +208,9 @@ export function relationFieldWriteOperation(fieldPath) {
|
|
|
199
208
|
return 'mentionmode.update';
|
|
200
209
|
if (/^baseagents\.[^.]+\.model$/.test(field))
|
|
201
210
|
return 'model.use';
|
|
202
|
-
if (/^baseagents\.[^.]+\.
|
|
211
|
+
if (/^baseagents\.[^.]+\.auxiliaryModel$/.test(field))
|
|
212
|
+
return 'model.use';
|
|
213
|
+
if (/^baseagents\.[^.]+\.(?:effort|reasoning|auxiliaryEffort)$/.test(field)) {
|
|
203
214
|
return 'model.effort';
|
|
204
215
|
}
|
|
205
216
|
return undefined;
|