evolcore 0.0.11 → 0.0.12
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/bin/codex-managed-hook.mjs +80 -0
- package/dist/agents/claude-runner.js +35 -2
- package/dist/agents/codex-app-server-client.js +120 -14
- package/dist/agents/codex-runner.js +273 -35
- package/dist/agents/ecagent-runner.js +36 -4
- package/dist/agents/gemini-runner.js +1 -0
- package/dist/aun/aid/client.js +11 -21
- package/dist/aun/aid/managed-operation.js +107 -0
- package/dist/aun/msg/group.js +81 -12
- package/dist/aun/msg/managed-operation.js +804 -0
- package/dist/aun/msg/mention-schema.js +20 -0
- package/dist/aun/msg/p2p.js +7 -0
- package/dist/channels/aun.js +288 -108
- package/dist/channels/daemon.js +13 -0
- package/dist/cli/agent-command.js +56 -8
- package/dist/cli/agent.js +170 -6
- package/dist/cli/aun-commands.js +270 -69
- package/dist/cli/bench.js +12 -3
- package/dist/cli/daemon-commands.js +31 -31
- package/dist/cli/fs-command.js +60 -0
- package/dist/cli/handoff-command.js +3 -0
- package/dist/cli/index.js +30 -2
- package/dist/cli/managed-command-guard.js +41 -0
- package/dist/cli/model.js +11 -4
- package/dist/cli/queue-command.js +11 -1
- package/dist/cli/response.js +71 -0
- package/dist/cli/restart-monitor.js +4 -18
- package/dist/cli/trigger-command.js +4 -0
- package/dist/config/builtin-roles.js +4 -0
- package/dist/config/contact-book-store.js +11 -3
- package/dist/config/contact-request-service.js +161 -15
- package/dist/config/mention-mode.js +8 -24
- package/dist/core/auth/agent-delegation.js +0 -1
- package/dist/core/auth/operation-authorizer.js +30 -7
- package/dist/core/auth/operation-catalog.js +187 -25
- package/dist/core/bootstrap-messages.js +18 -0
- package/dist/core/bootstrap-service.js +63 -4
- package/dist/core/capability/providers/claude-capability-provider.js +9 -31
- package/dist/core/capability/providers/codex-capability-provider.js +4 -15
- package/dist/core/capability/skill-discovery.js +55 -0
- package/dist/core/channel-loader.js +1 -1
- package/dist/core/command/cli-intent-parser.js +9 -0
- package/dist/core/command/command-handler.js +467 -6
- package/dist/core/command/connect-menu.js +15 -0
- package/dist/core/command/group-menu.js +4 -4
- package/dist/core/command/menu-catalog.js +366 -0
- package/dist/core/command/menu-handler.js +147 -35
- package/dist/core/command/menu-protocol.js +71 -0
- package/dist/core/command/slash-gate.js +5 -2
- package/dist/core/command/slash-handler.js +41 -21
- package/dist/core/daemon-file-cache.js +2 -1
- package/dist/core/data-migration.js +9 -2
- package/dist/core/evolagent.js +4 -2
- package/dist/core/handoff/dispatcher.js +5 -2
- package/dist/core/handoff/runtime.js +145 -21
- package/dist/core/handoff/store.js +24 -0
- package/dist/core/handoff/types.js +1 -0
- package/dist/core/message/logical-queue-bridge.js +1 -0
- package/dist/core/message/mention-schema.js +126 -0
- package/dist/core/message/message-bridge.js +236 -78
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/message-queue.js +1 -0
- package/dist/core/message/response-engine.js +54 -36
- package/dist/core/permission/ec-command-parser.js +156 -10
- package/dist/core/permission/sandbox-runtime.js +22 -1
- package/dist/core/permission/tool-policy.js +73 -30
- package/dist/core/protected-paths.js +7 -2
- package/dist/core/session/session-manager.js +21 -2
- package/dist/eck/manifest-engine.js +39 -13
- package/dist/index.js +384 -98
- package/dist/ipc.js +218 -9
- package/dist/response-system/coordinator.js +6 -4
- package/dist/response-system/engines/v1/proactive-flow.js +26 -8
- package/dist/response-system/modes/single-session/index.js +14 -1
- package/dist/response-system/selector.js +6 -5
- package/dist/trigger/anomaly-store.js +4 -0
- package/dist/trigger/script-executor.js +1 -0
- package/dist/utils/codex-app-server-registry.js +90 -0
- package/dist/utils/codex-cli.js +5 -1
- package/dist/utils/cross-platform.js +15 -0
- package/dist/utils/npm-ops.js +5 -12
- package/dist/utils/tool-summary.js +11 -0
- package/kits/docs/channels/aun.md +1 -1
- package/kits/docs/context-assembly.md +2 -2
- package/kits/docs/evolcore/agent.md +11 -6
- package/kits/docs/evolcore/aid.md +14 -0
- package/kits/docs/prompt-loading-architecture.md +1 -2
- package/kits/docs/venues/group.md +1 -1
- package/kits/eck_manifest.json +0 -12
- package/kits/rules/04-relation.md +1 -1
- package/kits/rules/05-venue.md +2 -2
- package/kits/schemas/single-session.schema.1.json +3 -3
- package/kits/schemas/single-session.schema.2.json +3 -3
- package/kits/templates/roles/admin.json +22 -0
- package/kits/templates/roles/member.json +27 -0
- package/kits/templates/roles/visitor.json +33 -3
- package/kits/templates/system-fragments/bootstrap.md +1 -1
- package/kits/templates/system-fragments/channel.md +1 -1
- package/kits/templates/system-fragments/session.md +1 -1
- package/package.json +2 -2
- package/dist/core/command/evol-menu-version-gate.js +0 -39
|
@@ -4,7 +4,7 @@ import { randomUUID } from 'crypto';
|
|
|
4
4
|
import { logger } from '../../utils/logger.js';
|
|
5
5
|
import { resolveRoot } from '../../paths.js';
|
|
6
6
|
import { containsHClassReference, containsLClassReference, getExistingHClassMaskTargets, hClassGrantIncludesProtectedPath, isHClassPath, isSameOrDescendant, isLClassPath, lClassGrantIncludesProtectedPath, resolveProtectedCandidate, } from '../protected-paths.js';
|
|
7
|
-
import { classifyEvolcoreShellCommand, parseBoundedOutputShellCommand, parseLiteralShellArgv, } from './ec-command-parser.js';
|
|
7
|
+
import { classifyEvolcoreShellCommand, parseCodexToolCommandArgv, parseBoundedOutputShellCommand, parseLiteralShellArgv, unwrapCodexShellCommandArgv, } from './ec-command-parser.js';
|
|
8
8
|
import { analyzeReadonlyShellQuery, } from './readonly-shell-query.js';
|
|
9
9
|
// 绝对禁止命令(所有角色、所有权限模式下都禁止,不可授权)
|
|
10
10
|
// 这些是系统级破坏操作,任何情况下都不应该允许
|
|
@@ -1071,30 +1071,57 @@ function prepareBoundedOutputInput(input, command, kind, context) {
|
|
|
1071
1071
|
export function evaluateToolPreflight(toolName, input, context) {
|
|
1072
1072
|
if (toolName === 'Bash') {
|
|
1073
1073
|
const command = typeof input.command === 'string' ? input.command : '';
|
|
1074
|
-
|
|
1074
|
+
const explicitCommandArgv = Array.isArray(input.commandArgv)
|
|
1075
|
+
&& input.commandArgv.every(value => typeof value === 'string')
|
|
1076
|
+
? input.commandArgv
|
|
1077
|
+
: undefined;
|
|
1078
|
+
const outerArgv = explicitCommandArgv ?? parseLiteralShellArgv(command) ?? undefined;
|
|
1079
|
+
const wrappedCommand = outerArgv ? unwrapCodexShellCommandArgv(outerArgv) : undefined;
|
|
1080
|
+
const policyCommand = wrappedCommand ?? command;
|
|
1081
|
+
if (context.sessionId && /^\s*(?:command\s+-v|which|type)\s+ec\s*$/i.test(policyCommand)) {
|
|
1075
1082
|
return {
|
|
1076
1083
|
behavior: 'deny',
|
|
1077
1084
|
input,
|
|
1078
1085
|
message: '🔒 EvolCore 托管会话不需要探测 `ec` 是否存在;请不要调用 `command -v`/`which`/`type`,也不要拼接其它命令。通信必须走当前 session 的 delegated sender。',
|
|
1086
|
+
policyCode: 'managed_ec_discovery_forbidden',
|
|
1079
1087
|
};
|
|
1080
1088
|
}
|
|
1081
|
-
const argv = context.sessionId ?
|
|
1082
|
-
const
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
&& argv[2] === 'agentmd'
|
|
1087
|
-
&& argv[3] === 'put'
|
|
1089
|
+
const argv = context.sessionId ? parseCodexToolCommandArgv(input) : null;
|
|
1090
|
+
const managedAidArgv = argv?.at(-2) === '--format' && argv.at(-1) === 'json'
|
|
1091
|
+
? argv.slice(0, -2)
|
|
1092
|
+
: argv;
|
|
1093
|
+
const ownAid = !!managedAidArgv
|
|
1088
1094
|
&& !!context.selfAid
|
|
1089
|
-
&&
|
|
1090
|
-
|
|
1095
|
+
&& managedAidArgv.at(-1)?.replace(/^@/, '') === context.selfAid.replace(/^@/, '');
|
|
1096
|
+
const isSelfManagedAidRead = !!managedAidArgv
|
|
1097
|
+
&& managedAidArgv[0] === 'ec'
|
|
1098
|
+
&& managedAidArgv[1] === 'aid'
|
|
1099
|
+
&& ownAid
|
|
1100
|
+
&& ((managedAidArgv.length === 4 && ['show', 'lookup'].includes(managedAidArgv[2]))
|
|
1101
|
+
|| (managedAidArgv.length === 5
|
|
1102
|
+
&& managedAidArgv[2] === 'agentmd'
|
|
1103
|
+
&& managedAidArgv[3] === 'get'));
|
|
1104
|
+
const isSelfAgentMdPublish = !!managedAidArgv
|
|
1105
|
+
&& managedAidArgv.length === 5
|
|
1106
|
+
&& managedAidArgv[0] === 'ec'
|
|
1107
|
+
&& managedAidArgv[1] === 'aid'
|
|
1108
|
+
&& managedAidArgv[2] === 'agentmd'
|
|
1109
|
+
&& managedAidArgv[3] === 'put'
|
|
1110
|
+
&& ownAid;
|
|
1111
|
+
const canPublishSelfAgentMd = isSelfAgentMdPublish
|
|
1112
|
+
&& ['request', 'bypass'].includes(context.permissionMode ?? '');
|
|
1113
|
+
const isAllowedManagedAidCommand = isSelfManagedAidRead || canPublishSelfAgentMd;
|
|
1114
|
+
if (context.sessionId && /^\s*ec\s+aid(?:\s|$)/i.test(policyCommand) && !isAllowedManagedAidCommand) {
|
|
1091
1115
|
return {
|
|
1092
1116
|
behavior: 'deny',
|
|
1093
1117
|
input,
|
|
1094
|
-
message:
|
|
1118
|
+
message: isSelfAgentMdPublish && (context.permissionMode === 'readonly' || context.permissionMode === 'auto')
|
|
1119
|
+
? `🔒 ${context.permissionMode} 模式禁止发布 agent.md;请切换到 request 或 bypass 后再执行 \`ec aid agentmd put <当前 self AID>\`。`
|
|
1120
|
+
: '🔒 EvolCore 托管会话仅允许针对当前 self AID 精确调用 `ec aid show`、`ec aid lookup` 或 `ec aid agentmd get`;`ec aid agentmd put` 还要求 request 或 bypass。可选后缀仅限 `--format json`。禁止枚举、切换或操作其他身份。',
|
|
1121
|
+
policyCode: 'managed_ec_aid_scope_forbidden',
|
|
1095
1122
|
};
|
|
1096
1123
|
}
|
|
1097
|
-
const ecCommand = classifyEvolcoreShellCommand(
|
|
1124
|
+
const ecCommand = classifyEvolcoreShellCommand(policyCommand);
|
|
1098
1125
|
if (ecCommand.kind === 'literal') {
|
|
1099
1126
|
return { behavior: 'allow', input, reason: 'ec-command' };
|
|
1100
1127
|
}
|
|
@@ -1102,25 +1129,40 @@ export function evaluateToolPreflight(toolName, input, context) {
|
|
|
1102
1129
|
return prepareBoundedOutputInput(input, ecCommand.command, 'ec', context);
|
|
1103
1130
|
}
|
|
1104
1131
|
if (ecCommand.kind === 'composite') {
|
|
1105
|
-
|
|
1132
|
+
// Older app-server approval callbacks may provide only the rendered
|
|
1133
|
+
// `/bin/bash -lc ...` command string. A composite inside that carrier
|
|
1134
|
+
// is not eligible for a delegation ticket, but owner bypass still
|
|
1135
|
+
// handles it as an ordinary shell request. Structured `commandArgv`
|
|
1136
|
+
// callbacks remain fail-closed below because their command boundary is
|
|
1137
|
+
// authoritative.
|
|
1138
|
+
const legacyRenderedCarrier = !explicitCommandArgv && wrappedCommand !== undefined;
|
|
1139
|
+
const allowLegacyBypassComposition = legacyRenderedCarrier
|
|
1140
|
+
&& context.permissionMode === 'bypass'
|
|
1141
|
+
&& ecCommand.issue === 'shell-composition';
|
|
1142
|
+
if (!allowLegacyBypassComposition) {
|
|
1143
|
+
if (ecCommand.issue === 'unsafe-expansion') {
|
|
1144
|
+
return {
|
|
1145
|
+
behavior: 'deny',
|
|
1146
|
+
input,
|
|
1147
|
+
message: '🔒 EC 双引号正文包含未转义的 Shell 展开;请将反引号写成 \\`,将 $()、${}、$变量中的 $ 写成 \\$。Shell 传给 ec 时会还原为原文字面量',
|
|
1148
|
+
policyCode: 'ec_shell_unsafe_expansion',
|
|
1149
|
+
};
|
|
1150
|
+
}
|
|
1151
|
+
if (ecCommand.issue === 'invalid-quote') {
|
|
1152
|
+
return {
|
|
1153
|
+
behavior: 'deny',
|
|
1154
|
+
input,
|
|
1155
|
+
message: '🔒 EC 命令引号未闭合或存在错误嵌套;正文中的双引号请写成 \\"',
|
|
1156
|
+
policyCode: 'ec_shell_invalid_quote',
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1106
1159
|
return {
|
|
1107
1160
|
behavior: 'deny',
|
|
1108
1161
|
input,
|
|
1109
|
-
message: '🔒 EC
|
|
1162
|
+
message: '🔒 EC 命令调用被拒绝:一次 Bash/exec_command 只能执行一条完整的 `ec` 命令。请直接调用 `ec ...`,不要先用 `command -v`/`ec aid` 探测,也不要使用 `&&`、`||`、`;`、`|`、重定向、变量展开、子 shell、`sh -c` 或 `bash -lc` 拼接其它命令。',
|
|
1163
|
+
policyCode: 'ec_shell_composite_command',
|
|
1110
1164
|
};
|
|
1111
1165
|
}
|
|
1112
|
-
if (ecCommand.issue === 'invalid-quote') {
|
|
1113
|
-
return {
|
|
1114
|
-
behavior: 'deny',
|
|
1115
|
-
input,
|
|
1116
|
-
message: '🔒 EC 命令引号未闭合或存在错误嵌套;正文中的双引号请写成 \\"',
|
|
1117
|
-
};
|
|
1118
|
-
}
|
|
1119
|
-
return {
|
|
1120
|
-
behavior: 'deny',
|
|
1121
|
-
input,
|
|
1122
|
-
message: '🔒 EC 命令调用被拒绝:一次 Bash/exec_command 只能执行一条完整的 `ec` 命令。请直接调用 `ec ...`,不要先用 `command -v`/`ec aid` 探测,也不要使用 `&&`、`||`、`;`、`|`、重定向、变量展开、子 shell、`sh -c` 或 `bash -lc` 拼接其它命令。',
|
|
1123
|
-
};
|
|
1124
1166
|
}
|
|
1125
1167
|
const boundedOutput = parseBoundedOutputShellCommand(command);
|
|
1126
1168
|
const hostProcessCommand = boundedOutput
|
|
@@ -1137,7 +1179,7 @@ export function evaluateToolPreflight(toolName, input, context) {
|
|
|
1137
1179
|
}
|
|
1138
1180
|
const blacklist = checkBlacklist(toolName, input);
|
|
1139
1181
|
if (blacklist.behavior === 'deny') {
|
|
1140
|
-
return { behavior: 'deny', input, message: blacklist.message };
|
|
1182
|
+
return { behavior: 'deny', input, message: blacklist.message, policyCode: 'tool_blacklist' };
|
|
1141
1183
|
}
|
|
1142
1184
|
const checkedInput = blacklist.updatedInput;
|
|
1143
1185
|
const hClass = checkHClassWrite(toolName, checkedInput, {
|
|
@@ -1150,7 +1192,7 @@ export function evaluateToolPreflight(toolName, input, context) {
|
|
|
1150
1192
|
root: context.root,
|
|
1151
1193
|
});
|
|
1152
1194
|
if (hClass.behavior === 'deny') {
|
|
1153
|
-
return { behavior: 'deny', input: checkedInput, message: hClass.message };
|
|
1195
|
+
return { behavior: 'deny', input: checkedInput, message: hClass.message, policyCode: 'h_class_protection' };
|
|
1154
1196
|
}
|
|
1155
1197
|
const lClass = checkLClassWrite(toolName, checkedInput, {
|
|
1156
1198
|
sessionId: context.sessionId,
|
|
@@ -1163,7 +1205,7 @@ export function evaluateToolPreflight(toolName, input, context) {
|
|
|
1163
1205
|
root: context.root,
|
|
1164
1206
|
});
|
|
1165
1207
|
if (lClass.behavior === 'deny') {
|
|
1166
|
-
return { behavior: 'deny', input: checkedInput, message: lClass.message };
|
|
1208
|
+
return { behavior: 'deny', input: checkedInput, message: lClass.message, policyCode: 'l_class_protection' };
|
|
1167
1209
|
}
|
|
1168
1210
|
const dangerous = checkDangerousCommand(toolName, checkedInput);
|
|
1169
1211
|
if (dangerous.isDangerous
|
|
@@ -1174,6 +1216,7 @@ export function evaluateToolPreflight(toolName, input, context) {
|
|
|
1174
1216
|
behavior: 'deny',
|
|
1175
1217
|
input: checkedInput,
|
|
1176
1218
|
message: '🔒 Git 破坏性操作仅允许 owner/admin 调用;当前角色无权执行',
|
|
1219
|
+
policyCode: 'role_git_destructive_forbidden',
|
|
1177
1220
|
};
|
|
1178
1221
|
}
|
|
1179
1222
|
return { behavior: 'continue', input: checkedInput };
|
|
@@ -11,7 +11,7 @@ const H_CLASS_RELATIVE_PATTERNS = [
|
|
|
11
11
|
/^backups\/config(?:\/|$)/,
|
|
12
12
|
/^\.snapshots(?:\/|$)/,
|
|
13
13
|
/^CA(?:\/|$)/,
|
|
14
|
-
/^(?:AIDs|aids)\/[^/]+\/(?:cert|keys)(?:\/|$)/,
|
|
14
|
+
/^(?:AIDs|aids)\/[^/]+\/(?:cert|keys|private)(?:\/|$)/,
|
|
15
15
|
/^\.device_id$/,
|
|
16
16
|
/^\.env$/,
|
|
17
17
|
/^\.seed(?:\.|$)/,
|
|
@@ -33,7 +33,7 @@ const H_CLASS_RELATIVE_PATTERNS = [
|
|
|
33
33
|
const H_CLASS_REFERENCE_PATTERNS = [
|
|
34
34
|
/(?:^|[^A-Za-z0-9_.-])daemon\.json(?=$|[^A-Za-z0-9_.-])/,
|
|
35
35
|
/(?:^|[^A-Za-z0-9_.-])agents[\\/](?:defaults\.json|[^/\\\s"']+[\\/](?:(?:config|contact)\.json|contact-operations\.jsonl|relations[\\/][^/\\\s"']+[\\/]config\.json))(?=$|[\s"';&|<>)},])/,
|
|
36
|
-
/(?:^|[^A-Za-z0-9_.-])(?:backups[\\/]config|\.snapshots|CA|(?:AIDs|aids)[\\/][^/\\\s"']+[\\/](?:cert|keys))(?:[\\/]|$|[\s"';&|<>)},])/,
|
|
36
|
+
/(?:^|[^A-Za-z0-9_.-])(?:backups[\\/]config|\.snapshots|CA|(?:AIDs|aids)[\\/][^/\\\s"']+[\\/](?:cert|keys|private))(?:[\\/]|$|[\s"';&|<>)},])/,
|
|
37
37
|
/(?:^|[/\\\s"'=<>])(?:\.device_id|\.env|\.seed(?:\.[^/\\\s"']*)?|\.migrated-[^/\\\s"']*|\.lock|daemon\.pid|data[\\/](?:causation-aun\.json(?:_|__)?|sessions|contact-book-audit\.jsonl|feishu-seen-[^/\\\s"']+\.jsonl|[a-z0-9_-]+-greeted-[^/\\\s"']+\.jsonl|message-queue\.json(?:_|__|\.tmp-[^/\\\s"']*)?|daemon[\\/](?:message-queue\.json(?:_|__|\.tmp-[^/\\\s"']*)?|control|migrations)|channel-state|instance[\\/]control\.token|outbox|handoff)|agents[\\/][^/\\\s"']+[\\/](?:sessions|data[\\/](?:channels|handoff|outbox\.jsonl|contact-audit\.jsonl)))(?:[\\/]|$|[\s"';&|<>)},])/,
|
|
38
38
|
/(?:^|[/\\\s"'=<>])data[\\/]triggers(?:[\\/]|$|[\s"';&|<>)},])/,
|
|
39
39
|
/(?:^|[/\\\s"'=<>])(?:[^/\\\s"']+\.json_|[^/\\\s"']+\.json\.migrated|defaults_\d+\.json)(?=$|[\s"';&|<>)},])/,
|
|
@@ -144,10 +144,14 @@ export function getHClassSandboxPatterns(root = resolveRoot()) {
|
|
|
144
144
|
path.join('AIDs', '*', 'cert', '**'),
|
|
145
145
|
path.join('AIDs', '*', 'keys'),
|
|
146
146
|
path.join('AIDs', '*', 'keys', '**'),
|
|
147
|
+
path.join('AIDs', '*', 'private'),
|
|
148
|
+
path.join('AIDs', '*', 'private', '**'),
|
|
147
149
|
path.join('aids', '*', 'cert'),
|
|
148
150
|
path.join('aids', '*', 'cert', '**'),
|
|
149
151
|
path.join('aids', '*', 'keys'),
|
|
150
152
|
path.join('aids', '*', 'keys', '**'),
|
|
153
|
+
path.join('aids', '*', 'private'),
|
|
154
|
+
path.join('aids', '*', 'private', '**'),
|
|
151
155
|
'.device_id',
|
|
152
156
|
'.env',
|
|
153
157
|
'.seed',
|
|
@@ -272,6 +276,7 @@ export function getExistingHClassMaskTargets(root = resolveRoot()) {
|
|
|
272
276
|
continue;
|
|
273
277
|
addExistingTarget(targets, path.join(aidsRoot, aid.name, 'cert'));
|
|
274
278
|
addExistingTarget(targets, path.join(aidsRoot, aid.name, 'keys'));
|
|
279
|
+
addExistingTarget(targets, path.join(aidsRoot, aid.name, 'private'));
|
|
275
280
|
}
|
|
276
281
|
}
|
|
277
282
|
catch { }
|
|
@@ -11,7 +11,7 @@ import fs from 'fs';
|
|
|
11
11
|
import os from 'os';
|
|
12
12
|
function newSessionStableMetadata(chatType, ...sources) {
|
|
13
13
|
const keys = chatType === 'group'
|
|
14
|
-
? ['channelKey', 'groupId', 'groupName', '
|
|
14
|
+
? ['channelKey', 'groupId', 'groupName', 'mentionMode']
|
|
15
15
|
: ['channelKey', 'peerId', 'peerName', 'peerType'];
|
|
16
16
|
const metadata = {};
|
|
17
17
|
for (const source of sources) {
|
|
@@ -1135,6 +1135,10 @@ export class SessionManager {
|
|
|
1135
1135
|
if (!baseagent) {
|
|
1136
1136
|
throw new Error(`[SessionManager] createNewSession: baseagent is empty for channel=${channel} channelId=${channelId}`);
|
|
1137
1137
|
}
|
|
1138
|
+
const metadata = newSessionStableMetadata(inheritedChatType, activeMetadata, identityMetadata);
|
|
1139
|
+
if (identityMetadata?.sessionBoundaryOperationId) {
|
|
1140
|
+
metadata.sessionBoundaryOperationId = identityMetadata.sessionBoundaryOperationId;
|
|
1141
|
+
}
|
|
1138
1142
|
const session = {
|
|
1139
1143
|
id: generateSessionId(),
|
|
1140
1144
|
channel,
|
|
@@ -1146,7 +1150,7 @@ export class SessionManager {
|
|
|
1146
1150
|
baseagent,
|
|
1147
1151
|
sessionKey: formatSessionKey(channelType, channelId, DEFAULT_THREAD_ID),
|
|
1148
1152
|
chatType: inheritedChatType,
|
|
1149
|
-
metadata
|
|
1153
|
+
metadata,
|
|
1150
1154
|
name: name || '默认会话',
|
|
1151
1155
|
createdAt: Date.now(),
|
|
1152
1156
|
updatedAt: Date.now(),
|
|
@@ -1163,6 +1167,21 @@ export class SessionManager {
|
|
|
1163
1167
|
});
|
|
1164
1168
|
return session;
|
|
1165
1169
|
}
|
|
1170
|
+
/**
|
|
1171
|
+
* Create and activate one fresh main session for a durable system operation.
|
|
1172
|
+
* Replaying the same operation returns the already-created session.
|
|
1173
|
+
*/
|
|
1174
|
+
async ensureNewMainSessionForOperation(opts) {
|
|
1175
|
+
const active = this.getActiveSessionSync(opts.channel, opts.channelId);
|
|
1176
|
+
if (active?.metadata?.sessionBoundaryOperationId === opts.operationId) {
|
|
1177
|
+
return { session: active, created: false };
|
|
1178
|
+
}
|
|
1179
|
+
const session = await this.createNewSession(opts.channel, opts.channelId, opts.projectPath, undefined, opts.baseagent, {
|
|
1180
|
+
...(opts.identityMetadata ?? {}),
|
|
1181
|
+
sessionBoundaryOperationId: opts.operationId,
|
|
1182
|
+
});
|
|
1183
|
+
return { session, created: true };
|
|
1184
|
+
}
|
|
1166
1185
|
async createForkedSession(sourceSession, forkedAgentSessionId, name) {
|
|
1167
1186
|
const channelType = sourceSession.channelType || sourceSession.channel;
|
|
1168
1187
|
const threadId = sourceSession.threadId || '';
|
|
@@ -55,6 +55,10 @@ function loadAndMergeManifest(filename) {
|
|
|
55
55
|
for (const s of base.sections)
|
|
56
56
|
merged.set(s.id, { ...s });
|
|
57
57
|
for (const s of override.sections) {
|
|
58
|
+
if (s.id === 'relation-group-profile' && merged.has('peer-profile')) {
|
|
59
|
+
logger.warn('[ManifestEngine] Ignoring deprecated patch section "relation-group-profile"; use "peer-profile" instead');
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
58
62
|
const existing = merged.get(s.id);
|
|
59
63
|
merged.set(s.id, existing ? { ...existing, ...s } : s);
|
|
60
64
|
}
|
|
@@ -320,15 +324,35 @@ function resolvePath(rawPath, vars) {
|
|
|
320
324
|
const r = resolvePathWithDiag(rawPath, vars);
|
|
321
325
|
return r.status === 'ok' ? r.resolved : null;
|
|
322
326
|
}
|
|
327
|
+
function isWithinDirectory(filePath, directory) {
|
|
328
|
+
const relative = path.relative(path.resolve(directory), path.resolve(filePath));
|
|
329
|
+
return relative === '' || (!path.isAbsolute(relative) && relative !== '..' && !relative.startsWith(`..${path.sep}`));
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* 随包发布的 kits 内容只在 reload/重启后刷新;personal/relations 及用户自定义
|
|
333
|
+
* manifest 路径属于运行时数据,必须用 mtime 检查,否则同一 daemon 内会长期陈旧。
|
|
334
|
+
*/
|
|
335
|
+
function sectionCacheOptions(resolvedPath, vars) {
|
|
336
|
+
if (isWithinDirectory(resolvedPath, kitsDir())) {
|
|
337
|
+
return { policy: 'on-reload', group: 'kits' };
|
|
338
|
+
}
|
|
339
|
+
const personalDir = typeof vars.PERSONAL_DIR === 'string' ? vars.PERSONAL_DIR : null;
|
|
340
|
+
const selfAid = typeof vars.selfAid === 'string' ? vars.selfAid : null;
|
|
341
|
+
if (personalDir && selfAid && isWithinDirectory(resolvedPath, personalDir)) {
|
|
342
|
+
return { policy: 'mtime', group: `agent-files:${selfAid}` };
|
|
343
|
+
}
|
|
344
|
+
return { policy: 'mtime', group: 'eck-runtime' };
|
|
345
|
+
}
|
|
323
346
|
/** 加载 loop 的 child 子模板文件内容(路径解析 + fileCache)。找不到返回 null。 */
|
|
324
347
|
export function loadChildTemplate(childFile, vars) {
|
|
325
348
|
const resolved = resolvePath(childFile, vars);
|
|
326
349
|
if (!resolved)
|
|
327
350
|
return null;
|
|
328
|
-
return fileCache.getText(resolved,
|
|
351
|
+
return fileCache.getText(resolved, sectionCacheOptions(resolved, vars));
|
|
329
352
|
}
|
|
330
353
|
/**
|
|
331
|
-
* 返回 [filePath, rawContent][]
|
|
354
|
+
* 返回 [filePath, rawContent][]。kits 文件跨 session 缓存到 reload;运行时文件
|
|
355
|
+
* (personal/relations/用户自定义路径)每次按 mtime 检查。
|
|
332
356
|
* 若传入 overflowOut 且目录段触发单目录限额(maxFiles/maxBytes),
|
|
333
357
|
* 会把溢出信息写入 overflowOut.value(不传则不启用限额收集,行为同旧版但仍应用默认上限)。
|
|
334
358
|
*/
|
|
@@ -343,7 +367,7 @@ export function loadSectionFiles(section, vars, sessionCache, overflowOut) {
|
|
|
343
367
|
return [];
|
|
344
368
|
const maxFiles = section.maxFiles ?? DIR_MAX_FILES;
|
|
345
369
|
const maxBytes = section.maxBytes ?? DIR_MAX_BYTES;
|
|
346
|
-
const { files, overflow } = readDirectoryFiles(resolved, section.pattern, maxFiles, maxBytes);
|
|
370
|
+
const { files, overflow } = readDirectoryFiles(resolved, section.pattern, maxFiles, maxBytes, vars);
|
|
347
371
|
if (overflow && overflowOut)
|
|
348
372
|
overflowOut.value = overflow;
|
|
349
373
|
return files.map(([name, content]) => [path.join(resolved, name), content]);
|
|
@@ -351,31 +375,33 @@ export function loadSectionFiles(section, vars, sessionCache, overflowOut) {
|
|
|
351
375
|
return [];
|
|
352
376
|
}
|
|
353
377
|
function loadFileSection(filePath, vars, sessionCache) {
|
|
354
|
-
void sessionCache; // 内容跨 session
|
|
378
|
+
void sessionCache; // 内容跨 session 共享,统一走全局 fileCache
|
|
355
379
|
const resolved = resolvePath(filePath, vars);
|
|
356
380
|
if (!resolved)
|
|
357
381
|
return null;
|
|
358
|
-
|
|
359
|
-
// 不再按 session 重复缓存同一文件内容。
|
|
360
|
-
const content = fileCache.getText(resolved, { policy: 'on-reload', group: 'kits' });
|
|
382
|
+
const content = fileCache.getText(resolved, sectionCacheOptions(resolved, vars));
|
|
361
383
|
return content === null ? null : [resolved, content];
|
|
362
384
|
}
|
|
363
385
|
/**
|
|
364
386
|
* 读取目录下匹配文件,受单目录限额(maxFiles/maxBytes)约束。
|
|
365
387
|
* 返回已加载文件 + 溢出信息(超限时)。按 fileCache 缓存目录列表与文件内容。
|
|
366
388
|
*/
|
|
367
|
-
function readDirectoryFiles(dirPath, pattern, maxFiles, maxBytes) {
|
|
389
|
+
function readDirectoryFiles(dirPath, pattern, maxFiles, maxBytes, vars) {
|
|
368
390
|
const glob = pattern || '*.md';
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
const names = fileCache.get(`${dirPath} ${glob}`, () => {
|
|
391
|
+
const cacheOptions = sectionCacheOptions(dirPath, vars);
|
|
392
|
+
const readNames = () => {
|
|
372
393
|
try {
|
|
373
394
|
return fs.readdirSync(dirPath).filter(f => matchGlob(f, glob)).sort();
|
|
374
395
|
}
|
|
375
396
|
catch {
|
|
376
397
|
return [];
|
|
377
398
|
}
|
|
378
|
-
}
|
|
399
|
+
};
|
|
400
|
+
// synthetic cache key 无法用文件 mtime 门控。kits 目录结构只在 reload 后刷新;
|
|
401
|
+
// 运行时目录直接重扫,确保新增/删除文件在同一 session 内可见。
|
|
402
|
+
const names = cacheOptions.policy === 'on-reload'
|
|
403
|
+
? fileCache.get(`${dirPath} ${glob}`, readNames, cacheOptions)
|
|
404
|
+
: readNames();
|
|
379
405
|
const out = [];
|
|
380
406
|
let usedBytes = 0;
|
|
381
407
|
let overflow;
|
|
@@ -387,7 +413,7 @@ function readDirectoryFiles(dirPath, pattern, maxFiles, maxBytes) {
|
|
|
387
413
|
break;
|
|
388
414
|
}
|
|
389
415
|
const fp = path.join(dirPath, f);
|
|
390
|
-
const content = fileCache.getText(fp,
|
|
416
|
+
const content = fileCache.getText(fp, sectionCacheOptions(fp, vars));
|
|
391
417
|
if (content === null)
|
|
392
418
|
continue;
|
|
393
419
|
// 字节限额(至少加载一个文件,避免单个超大文件导致空目录)
|