evolcore 0.0.32 → 0.0.34
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 +61 -0
- package/bin/ec.js +3 -2
- package/bin/install-codex-managed-hooks.mjs +135 -37
- package/dist/agents/baseagent-availability.js +20 -36
- package/dist/agents/claude-runner.js +111 -15
- package/dist/agents/codex/app-server-client.js +3 -0
- package/dist/agents/codex/diagnostics.js +112 -54
- package/dist/agents/codex-runner.js +241 -63
- package/dist/agents/ecagent-runner.js +78 -33
- package/dist/agents/runner-types.js +5 -6
- package/dist/aun/aid/identity.js +1 -1
- package/dist/aun/group/operations.js +0 -1
- package/dist/aun/msg/history.js +1 -0
- package/dist/aun/msg/p2p.js +0 -1
- package/dist/aun/outbox.js +50 -1
- package/dist/channels/aun.js +44 -8
- package/dist/channels/daemon.js +261 -3
- package/dist/cli/agent-command.js +58 -24
- package/dist/cli/agent.js +1 -1
- package/dist/cli/aun-commands.js +368 -31
- package/dist/cli/contact.js +8 -2
- package/dist/cli/ctl-command.js +3 -1
- package/dist/cli/daemon-commands.js +197 -27
- package/dist/cli/debug-mode.js +154 -0
- package/dist/cli/fs-command.js +2 -1
- package/dist/cli/handoff-command.js +3 -0
- package/dist/cli/index.js +98 -67
- package/dist/cli/init.js +2 -2
- package/dist/cli/integrity-command.js +196 -0
- package/dist/cli/managed-command-guard.js +5 -0
- package/dist/cli/memory-command.js +166 -146
- package/dist/cli/queue-command.js +9 -4
- package/dist/cli/session-command.js +194 -0
- package/dist/cli/skill-command.js +78 -2
- package/dist/cli/task-context.js +1 -0
- package/dist/cli/thread-command.js +622 -0
- package/dist/cli/trigger-command.js +10 -3
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-field-policy.js +6 -3
- package/dist/config/config-manager.js +26 -2
- package/dist/config/contact-book-store.js +5 -0
- package/dist/config/resolved-config-op.js +3 -0
- package/dist/config/role-service.js +13 -0
- package/dist/config/runtime-env.js +16 -0
- package/dist/config-store.js +45 -0
- package/dist/core/audit/log-integrity.js +3 -1
- package/dist/core/auth/agent-delegation.js +133 -36
- package/dist/core/auth/debug-context.js +139 -0
- package/dist/core/auth/delegation-diagnostics.js +414 -0
- package/dist/core/auth/operation-authorizer.js +25 -2
- package/dist/core/auth/operation-catalog.js +49 -0
- package/dist/core/background-session/session-cleanup.js +212 -0
- package/dist/core/background-session/types.js +10 -0
- package/dist/core/bootstrap-messages.js +1 -1
- package/dist/core/bootstrap-service.js +6 -3
- package/dist/core/command/agent-control.js +18 -26
- package/dist/core/command/command-handler.js +197 -27
- package/dist/core/command/daemon-diagnostics.js +1 -0
- package/dist/core/command/memory-slash.js +6 -2
- package/dist/core/command/menu-catalog.js +51 -1
- package/dist/core/command/menu-handler.js +114 -23
- package/dist/core/command/menu-protocol.js +25 -1
- package/dist/core/command/skill-slash.js +322 -0
- package/dist/core/command/slash-gate.js +6 -4
- package/dist/core/command/slash-handler.js +195 -13
- package/dist/core/command/topic-slash.js +528 -0
- package/dist/core/daemon-bootstrap.js +4 -7
- package/dist/core/evolagent-registry.js +20 -5
- package/dist/core/file-integrity/read.js +61 -0
- package/dist/core/file-integrity/store.js +262 -0
- package/dist/core/handoff/runtime.js +5 -11
- package/dist/core/handoff/store.js +5 -0
- package/dist/core/ipc-registration.js +175 -3
- package/dist/core/memory/batch-meta.js +132 -0
- package/dist/core/memory/consolidation-context.js +83 -29
- package/dist/core/memory/consolidation-ops-records.js +49 -16
- package/dist/core/memory/consolidation-queue-format.js +21 -0
- package/dist/core/memory/consolidation.js +264 -205
- package/dist/core/memory/index-manager.js +19 -28
- package/dist/core/memory/memory-admin.js +19 -14
- package/dist/core/memory/memory-document.js +53 -9
- package/dist/core/memory/memory-writer.js +30 -15
- package/dist/core/message/aun-ipc-sender.js +161 -6
- package/dist/core/message/im-renderer.js +169 -10
- package/dist/core/message/message-bridge.js +15 -4
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/peer-mode.js +6 -1
- package/dist/core/message/response-engine.js +929 -110
- package/dist/core/message/subtask-tool-projection.js +140 -0
- package/dist/core/model/catalog-service.js +17 -12
- package/dist/core/permission/ec-command-parser.js +217 -9
- package/dist/core/permission/protected-paths.js +5 -1
- package/dist/core/permission/readonly-shell-query.js +43 -5
- package/dist/core/permission/session-path-grants.js +334 -0
- package/dist/core/permission/tool-canonical.js +226 -0
- package/dist/core/permission/tool-policy.js +30 -62
- package/dist/core/role/runtime-policy.js +21 -10
- package/dist/core/session/session-fs-store.js +123 -8
- package/dist/core/session/session-idle-monitor.js +249 -22
- package/dist/core/session/session-manager.js +129 -14
- package/dist/core/skill/command-parser.js +13 -0
- package/dist/core/skill/index-generator.js +21 -4
- package/dist/core/skill/startup.js +41 -0
- package/dist/core/skill/switch-resolver.js +55 -29
- package/dist/core/system-session/grants.js +40 -0
- package/dist/core/system-session/memory.js +222 -0
- package/dist/core/system-session/registry.js +74 -0
- package/dist/core/system-session/types.js +14 -0
- package/dist/core/thread/command-parser.js +125 -0
- package/dist/core/thread/index.js +1 -0
- package/dist/core/thread/ipc-operation.js +95 -0
- package/dist/core/thread/thread-context-storage.js +131 -0
- package/dist/core/thread/thread-operation.js +258 -0
- package/dist/core/thread/thread-route.js +33 -0
- package/dist/core/thread/thread-service.js +770 -0
- package/dist/core/thread/types.js +1 -0
- package/dist/eck/kit-renderer.js +7 -2
- package/dist/eck/manifest-engine.js +24 -4
- package/dist/eck/message-renderer.js +35 -0
- package/dist/index.js +19 -7
- package/dist/ipc.js +140 -50
- package/dist/paths.js +93 -0
- package/dist/response-system/engines/v1/proactive-flow.js +169 -80
- package/dist/stats/billing.js +20 -8
- package/dist/trigger/authorization.js +57 -28
- package/dist/trigger/feedback.js +13 -2
- package/dist/trigger/parser.js +70 -1
- package/dist/trigger/system-definitions.js +3 -3
- package/dist/trigger/validation.js +14 -0
- package/dist/utils/cross-platform.js +11 -2
- package/dist/utils/error-dict.json +18 -4
- package/dist/utils/error-utils.js +143 -1
- package/dist/utils/instance-registry.js +13 -8
- package/dist/utils/log-writer.js +12 -0
- package/dist/utils/process-introspect.js +11 -0
- package/kits/docs/context-assembly.md +1 -1
- package/kits/docs/evolcore/INDEX.md +1 -0
- package/kits/docs/evolcore/agent-create.md +40 -28
- package/kits/docs/evolcore/agent.md +21 -6
- package/kits/docs/evolcore/config.md +2 -2
- package/kits/docs/evolcore/ctl.md +2 -2
- package/kits/docs/evolcore/ec-capabilities.md +5 -1
- package/kits/docs/evolcore/fs-architecture.md +4 -5
- package/kits/docs/evolcore/fs.md +1 -1
- package/kits/docs/evolcore/group-collaboration.md +4 -6
- package/kits/docs/evolcore/group.md +11 -12
- package/kits/docs/evolcore/memory-architecture.md +4 -5
- package/kits/docs/evolcore/memory-consolidation.md +19 -15
- package/kits/docs/evolcore/memory-tags.md +3 -1
- package/kits/docs/evolcore/memory.md +24 -12
- package/kits/docs/evolcore/menu-exec-schema-commands.md +2 -2
- package/kits/docs/evolcore/menu-protocol.md +7 -0
- package/kits/docs/evolcore/model.md +16 -0
- package/kits/docs/evolcore/msg.md +8 -11
- package/kits/docs/evolcore/thread-chain.md +267 -0
- package/kits/docs/prompt-loading-architecture.md +1 -1
- package/kits/eck_manifest.json +55 -40
- package/kits/eck_manifest.memory.json +0 -12
- package/kits/eck_message_manifest.json +169 -10
- package/kits/rules/01-overview.md +11 -0
- package/kits/rules/03-identity.md +6 -3
- package/kits/rules/05-venue.md +1 -1
- package/kits/rules/06-channel.md +1 -1
- package/kits/rules/07-memory-and-thread.md +199 -0
- package/kits/schemas/_meta.json +7 -1
- package/kits/schemas/agent-config.schema.13.json +1 -1
- package/kits/schemas/daemon.schema.7.json +140 -0
- package/kits/schemas/relation-config.schema.9.json +18 -3
- package/kits/schemas/role-config.schema.1.json +6 -6
- package/kits/schemas/single-session.schema.1.json +6 -6
- package/kits/schemas/single-session.schema.2.json +6 -6
- package/kits/schemas/single-session.schema.3.json +6 -6
- package/kits/skills/.switches.json +12 -1
- package/kits/skills/ec-group/SKILL.md +234 -0
- package/kits/skills/ec-skill/SKILL.md +10 -1
- package/kits/skills/ec-skill/scripts/cli.mjs +16 -2
- package/kits/skills/ec-skill/scripts/skill.mjs +1 -1
- package/kits/skills/scrapling-article-fetch/SKILL.md +128 -0
- package/kits/skills/scrapling-article-fetch/scripts/check_python_env.py +54 -0
- package/kits/skills/scrapling-article-fetch/scripts/render_markdown_reply.py +35 -0
- package/kits/skills/scrapling-article-fetch/scripts/run_pipeline.sh +16 -0
- package/kits/skills/scrapling-article-fetch/scripts/scrapling_fetch.py +344 -0
- package/kits/skills/thread-ops/SKILL.md +492 -0
- package/kits/skills/wechat-article-media-download/SKILL.md +65 -0
- package/kits/skills/wechat-article-media-download/scripts/download.py +163 -0
- package/kits/skills/wechat-article-reader/SKILL.md +97 -0
- package/kits/skills/wechat-article-reader/scripts/fetch.mjs +256 -0
- package/kits/templates/bootstrap-welcome.md +7 -7
- package/kits/templates/memory-consolidation/forget.md +7 -2
- package/kits/templates/memory-consolidation/generalize.md +9 -4
- package/kits/templates/memory-consolidation/notes.md +11 -11
- package/kits/templates/memory-consolidation/profile.md +8 -3
- package/kits/templates/memory-consolidation/promote.md +8 -3
- package/kits/templates/memory-consolidation/workflow.md +6 -6
- package/kits/templates/message-fragments/handoff-request-to-target.md +1 -1
- package/kits/templates/message-fragments/item.md +2 -1
- package/kits/templates/message-fragments/thread-context.md +6 -0
- package/kits/templates/message-fragments/trigger-session.md +2 -0
- package/kits/templates/message-fragments/trigger-thread-check.md +2 -0
- package/kits/templates/roles/member.json +2 -0
- package/kits/templates/roles/visitor.json +2 -0
- package/kits/templates/system-fragments/bootstrap.md +67 -72
- package/kits/templates/system-fragments/channel.md +3 -6
- package/kits/templates/system-fragments/commands.md +1 -0
- package/kits/templates/system-fragments/managed-runtime.md +15 -0
- package/kits/templates/system-fragments/memory.md +16 -9
- package/kits/templates/system-fragments/reply.md +7 -6
- package/kits/templates/system-fragments/session.md +5 -14
- package/package.json +6 -3
- package/dist/core/memory/memory-session-policy.js +0 -41
- package/dist/core/memory/memory-signature.js +0 -257
- package/kits/docs/evolcore/group-fs.md +0 -17
- package/kits/docs/venues/aun-group.md +0 -45
- package/kits/docs/venues/group.md +0 -43
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner 子任务(Claude 后台任务/子智能体/工作流、Codex 子智能体)的出站投影。
|
|
3
|
+
*
|
|
4
|
+
* 子任务本身就是某次工具调用的执行过程:Claude 的 task_started / task_notification
|
|
5
|
+
* 都带来源工具的 tool_use_id。实测两种时序(events.log):
|
|
6
|
+
* - 前台:tool_use → task_started → task_notification → 真实 tool_result(数毫秒后)
|
|
7
|
+
* - 后台:tool_use → task_started → 占位 tool_result("running in background")→ … → task_notification
|
|
8
|
+
*
|
|
9
|
+
* 因此有来源工具时,子任务不再单独出卡片,而是**归拢到来源工具的 tool_result**:
|
|
10
|
+
* 终态合并进那条 tool_result(见 foldSubtaskTerminal),后台场景先扣住占位结果,等终态再发。
|
|
11
|
+
* 渲染器负责扣留与合并(IMRenderer.beginParentSubtask / endParentSubtask)。
|
|
12
|
+
*
|
|
13
|
+
* 没有来源工具(如 Codex 子智能体只有子线程 ID)时,退回合成卡片:
|
|
14
|
+
* 开始一条 tool_call,终态一条 tool_result,共用 call_id = subtask-<taskId>。
|
|
15
|
+
*/
|
|
16
|
+
/** 合成 call_id 前缀,避免与真实工具调用 ID 相撞。 */
|
|
17
|
+
const SUBTASK_CALL_ID_PREFIX = 'subtask';
|
|
18
|
+
/** 描述作为命令摘要展示时的字符上限。 */
|
|
19
|
+
const SHORT_DESCRIPTION_MAX_CHARS = 120;
|
|
20
|
+
/** 终态摘要作为 tool_result.result 展示时的字符上限。 */
|
|
21
|
+
const RESULT_SUMMARY_MAX_CHARS = 500;
|
|
22
|
+
/** 无来源工具时,同一子任务开始与终态共用的合成 call_id。 */
|
|
23
|
+
export function subtaskCallId(taskId) {
|
|
24
|
+
return `${SUBTASK_CALL_ID_PREFIX}-${taskId}`;
|
|
25
|
+
}
|
|
26
|
+
/** 子任务在工具卡片上的显示名(仅用于合成卡片或拿不到来源工具名时)。 */
|
|
27
|
+
export function subtaskToolName(input) {
|
|
28
|
+
if (input.taskKind === 'agent')
|
|
29
|
+
return input.subagentType || 'Agent';
|
|
30
|
+
if (input.taskKind === 'workflow')
|
|
31
|
+
return 'Workflow';
|
|
32
|
+
if (input.taskType === 'local_bash')
|
|
33
|
+
return 'Bash';
|
|
34
|
+
return input.taskType || 'Task';
|
|
35
|
+
}
|
|
36
|
+
function collapse(text) {
|
|
37
|
+
return text ? text.replace(/\s+/g, ' ').trim() : '';
|
|
38
|
+
}
|
|
39
|
+
function truncate(text, maxChars) {
|
|
40
|
+
if (text.length <= maxChars)
|
|
41
|
+
return text;
|
|
42
|
+
return `${text.slice(0, maxChars)}…`;
|
|
43
|
+
}
|
|
44
|
+
/** 把描述压成一行短摘要:去掉换行与多余空白,超长截断。 */
|
|
45
|
+
export function shortenSubtaskDescription(description) {
|
|
46
|
+
return truncate(collapse(description), SHORT_DESCRIPTION_MAX_CHARS);
|
|
47
|
+
}
|
|
48
|
+
function formatDuration(durationMs) {
|
|
49
|
+
if (typeof durationMs !== 'number' || !Number.isFinite(durationMs) || durationMs <= 0)
|
|
50
|
+
return '';
|
|
51
|
+
if (durationMs < 1000)
|
|
52
|
+
return `${Math.round(durationMs)}ms`;
|
|
53
|
+
return `${Math.round(durationMs / 1000)}s`;
|
|
54
|
+
}
|
|
55
|
+
/** 终态文案。状态由 ok/detail_code 表达,客户端不需要解析正文。 */
|
|
56
|
+
export function subtaskStatusText(status, durationMs) {
|
|
57
|
+
const duration = formatDuration(durationMs);
|
|
58
|
+
const label = status === 'completed' ? '子任务完成'
|
|
59
|
+
: status === 'failed' ? '子任务失败'
|
|
60
|
+
: status === 'killed' ? '子任务已终止'
|
|
61
|
+
: '子任务已停止';
|
|
62
|
+
return duration ? `${label} (${duration})` : label;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 把子任务终态合并进来源工具的 tool_result。
|
|
66
|
+
*
|
|
67
|
+
* - ok:工具结果与子任务都成功才为 true。子任务失败/停止/终止时一律 false——实测存在
|
|
68
|
+
* "任务 failed、工具结果 isError=false" 的组合,不能用工具的成功覆盖子任务的失败。
|
|
69
|
+
* - 工具结果已带错误分类时保留原分类;工具成功而子任务失败时补 EXECUTION_FAILED。
|
|
70
|
+
* 停止与终止不是执行失败,只用 detail_code 区分。
|
|
71
|
+
* - 正文、result、text 保留来源工具原值;子任务耗时更长(后台任务)时以子任务为准。
|
|
72
|
+
*/
|
|
73
|
+
export function foldSubtaskTerminal(item, terminal) {
|
|
74
|
+
if (item.kind !== 'tool_result')
|
|
75
|
+
return item;
|
|
76
|
+
const merged = { ...item };
|
|
77
|
+
if (typeof terminal.durationMs === 'number' && terminal.durationMs > (merged.duration_ms ?? 0)) {
|
|
78
|
+
merged.duration_ms = terminal.durationMs;
|
|
79
|
+
}
|
|
80
|
+
if (terminal.status === 'completed')
|
|
81
|
+
return merged;
|
|
82
|
+
merged.ok = false;
|
|
83
|
+
merged.detail_code ??= `subtask_${terminal.status}`;
|
|
84
|
+
if (!merged.error_code && terminal.status === 'failed')
|
|
85
|
+
merged.error_code = 'EXECUTION_FAILED';
|
|
86
|
+
if (!merged.error) {
|
|
87
|
+
const summary = collapse(terminal.summary);
|
|
88
|
+
merged.error = summary ? truncate(summary, RESULT_SUMMARY_MAX_CHARS) : subtaskStatusText(terminal.status);
|
|
89
|
+
}
|
|
90
|
+
return merged;
|
|
91
|
+
}
|
|
92
|
+
function buildArguments(input, shortDescription) {
|
|
93
|
+
const args = {
|
|
94
|
+
task_id: input.taskId,
|
|
95
|
+
subtask_kind: input.taskKind,
|
|
96
|
+
};
|
|
97
|
+
if (shortDescription)
|
|
98
|
+
args.command = shortDescription;
|
|
99
|
+
if (input.taskType)
|
|
100
|
+
args.task_type = input.taskType;
|
|
101
|
+
if (input.subagentType)
|
|
102
|
+
args.subagent_type = input.subagentType;
|
|
103
|
+
if (input.workflowName)
|
|
104
|
+
args.workflow_name = input.workflowName;
|
|
105
|
+
return args;
|
|
106
|
+
}
|
|
107
|
+
/** 无来源工具的子任务开始 → 合成 tool_call。 */
|
|
108
|
+
export function projectSubtaskStarted(input) {
|
|
109
|
+
const shortDescription = shortenSubtaskDescription(input.description);
|
|
110
|
+
return {
|
|
111
|
+
kind: 'tool_call',
|
|
112
|
+
call_id: subtaskCallId(input.taskId),
|
|
113
|
+
name: subtaskToolName(input),
|
|
114
|
+
arguments: buildArguments(input, shortDescription),
|
|
115
|
+
text: shortDescription,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* 子任务终态 → 独立 tool_result。
|
|
120
|
+
*
|
|
121
|
+
* 用于两种情况:无来源工具的合成卡片;或来源工具卡片已在本轮之外/已提前释放,
|
|
122
|
+
* 只能补发一条同 call_id 的终态结果来更新那张卡片。
|
|
123
|
+
*/
|
|
124
|
+
export function projectSubtaskTerminal(input) {
|
|
125
|
+
const shortDescription = shortenSubtaskDescription(input.description);
|
|
126
|
+
// 与描述比较用未截断的折叠文本:local_bash 未给 description 时 SDK 会把整条命令
|
|
127
|
+
// 同时塞进 description 和 summary,两者只是截断长度不同,仍属同一段文字。
|
|
128
|
+
const rawSummary = collapse(input.summary);
|
|
129
|
+
const summary = rawSummary ? truncate(rawSummary, RESULT_SUMMARY_MAX_CHARS) : '';
|
|
130
|
+
const base = {
|
|
131
|
+
kind: 'tool_result',
|
|
132
|
+
call_id: input.callId ?? subtaskCallId(input.taskId),
|
|
133
|
+
name: input.toolName ?? subtaskToolName(input),
|
|
134
|
+
ok: true,
|
|
135
|
+
text: `${subtaskStatusText(input.status, input.durationMs)}${shortDescription ? ` · ${shortDescription}` : ''}`,
|
|
136
|
+
};
|
|
137
|
+
if (summary && rawSummary !== collapse(input.description))
|
|
138
|
+
base.result = summary;
|
|
139
|
+
return foldSubtaskTerminal(base, { status: input.status, summary, durationMs: input.durationMs });
|
|
140
|
+
}
|
|
@@ -13,14 +13,19 @@ export const CATALOG_ROUTE_TIMEOUT_MS = 15_000;
|
|
|
13
13
|
* 否则旧规则写下的持久化快照与新快照无法区分,会继续被服务。
|
|
14
14
|
* v2:保留上下文窗口后缀(`claude-opus-5[1m]` 不再被折叠成基础 ID)。
|
|
15
15
|
* v3:保留 Codex 上游声明的 max effort,重新获取旧快照中过滤掉的能力。
|
|
16
|
+
* v4:统一缺失 effort 元数据时的 transport fallback,并记录来源。
|
|
16
17
|
*/
|
|
17
|
-
export const CATALOG_SNAPSHOT_VERSION =
|
|
18
|
+
export const CATALOG_SNAPSHOT_VERSION = 4;
|
|
19
|
+
export const CONFIGURABLE_EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max'];
|
|
18
20
|
export const TRANSPORT_EFFORTS = {
|
|
19
21
|
claude: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
20
22
|
codex: ['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'],
|
|
21
23
|
ecagent: ['minimal', 'low', 'medium', 'high', 'xhigh', 'max'],
|
|
22
24
|
gemini: [],
|
|
23
25
|
};
|
|
26
|
+
export function configurableEfforts(baseagent, efforts = TRANSPORT_EFFORTS[baseagent] ?? []) {
|
|
27
|
+
return efforts.filter((effort) => CONFIGURABLE_EFFORTS.includes(effort) && (TRANSPORT_EFFORTS[baseagent] ?? []).includes(effort));
|
|
28
|
+
}
|
|
24
29
|
function stable(value) {
|
|
25
30
|
if (Array.isArray(value))
|
|
26
31
|
return `[${value.map(stable).join(',')}]`;
|
|
@@ -93,10 +98,7 @@ const BUILTIN_IDS = {
|
|
|
93
98
|
gemini: ['gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-2.5-flash-lite'],
|
|
94
99
|
};
|
|
95
100
|
export function builtinCatalog(baseagent) {
|
|
96
|
-
return (BUILTIN_IDS[baseagent] ?? []).map(id => ({ id, verification: 'unverified'
|
|
97
|
-
...(baseagent === 'codex' ? { reasoningLevels: ['low', 'medium', 'high', 'xhigh'] } : {}),
|
|
98
|
-
...(/^claude-(opus|sonnet|haiku)-/.test(id) && baseagent === 'claude' ? { reasoningLevels: [...TRANSPORT_EFFORTS.claude] } : {}),
|
|
99
|
-
}));
|
|
101
|
+
return (BUILTIN_IDS[baseagent] ?? []).map(id => ({ id, verification: 'unverified' }));
|
|
100
102
|
}
|
|
101
103
|
export function deriveAliases(models, baseagent) {
|
|
102
104
|
if (baseagent !== 'claude')
|
|
@@ -126,16 +128,19 @@ function compatible(model, baseagent) {
|
|
|
126
128
|
return !model.protocols || !protocols[baseagent] || model.protocols.some(p => protocols[baseagent].includes(p));
|
|
127
129
|
}
|
|
128
130
|
function adapt(models, baseagent) {
|
|
129
|
-
const available = models.filter(m => compatible(m, baseagent)).map(m =>
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
const available = models.filter(m => compatible(m, baseagent)).map(m => {
|
|
132
|
+
const reasoningLevels = configurableEfforts(baseagent, m.reasoningLevels);
|
|
133
|
+
const effortSource = reasoningLevels.length === 0 ? 'none'
|
|
134
|
+
: m.reasoningLevels === undefined ? 'transport-fallback' : 'catalog';
|
|
135
|
+
return { ...m, reasoningLevels, effortSource };
|
|
136
|
+
});
|
|
132
137
|
const aliases = deriveAliases(available, baseagent);
|
|
133
138
|
return [...Object.entries(aliases).filter(([id]) => !available.some(m => m.id === id)).map(([id, aliasFor]) => ({ ...available.find(m => m.id === aliasFor), id, aliasFor, owned_by: 'alias' })), ...available];
|
|
134
139
|
}
|
|
135
140
|
export function resolveCatalogModel(snapshot, model) { return snapshot.models.find(m => m.id === model)?.aliasFor ?? model; }
|
|
136
141
|
export function catalogEfforts(snapshot, model, baseagent) {
|
|
137
142
|
const entry = snapshot.models.find(m => m.id === resolveCatalogModel(snapshot, model));
|
|
138
|
-
return entry
|
|
143
|
+
return entry ? configurableEfforts(baseagent, entry.reasoningLevels) : [];
|
|
139
144
|
}
|
|
140
145
|
export function assertCatalogSelectable(snapshot) {
|
|
141
146
|
if (snapshot.diagnostics.some(d => d.code === 'AUTH_FAILED'))
|
|
@@ -288,7 +293,7 @@ export class ModelCatalogService {
|
|
|
288
293
|
// suggestions; never let it attest gateway membership for another key.
|
|
289
294
|
for (const [family, id] of Object.entries(aliases))
|
|
290
295
|
if (typeof id === 'string' && /^(opus|sonnet|haiku)$/.test(family) && new RegExp(`^claude-${family}-[A-Za-z0-9._-]+$`).test(id) && !models.some(m => m.id === id))
|
|
291
|
-
models.unshift({ id, verification: 'unverified'
|
|
296
|
+
models.unshift({ id, verification: 'unverified' });
|
|
292
297
|
}
|
|
293
298
|
catch { /* No legacy data. */ }
|
|
294
299
|
return models;
|
|
@@ -302,8 +307,8 @@ export class ModelCatalogService {
|
|
|
302
307
|
const models = await gatewayModels(context, this.options.fetch ?? fetch);
|
|
303
308
|
// Enrich only IDs the gateway listed. Native models never add/remove membership.
|
|
304
309
|
const native = context.baseagent === 'codex' && models.length ? await this.options.native?.(context).catch(() => null) : null;
|
|
305
|
-
const metadata = new Map((native ??
|
|
306
|
-
const enriched = models.map(m => ({ ...m, ...(m.reasoningLevels === undefined && metadata.get(m.id)?.reasoningLevels ? { reasoningLevels: metadata.get(m.id).reasoningLevels } : {}) }));
|
|
310
|
+
const metadata = new Map((native ?? []).map(m => [m.id, m]));
|
|
311
|
+
const enriched = models.map(m => ({ ...m, ...(m.reasoningLevels === undefined && metadata.get(m.id)?.reasoningLevels !== undefined ? { reasoningLevels: metadata.get(m.id).reasoningLevels } : {}) }));
|
|
307
312
|
const result = this.make(context, 'gateway', 'fresh', enriched, diagnostics);
|
|
308
313
|
this.save(context, result);
|
|
309
314
|
return result;
|
|
@@ -663,7 +663,7 @@ const INLINE_TEXT_FLAGS = {
|
|
|
663
663
|
};
|
|
664
664
|
const MESSAGE_POSITIONAL_FLAGS = new Set([
|
|
665
665
|
'--file', '--payload', '--as', '--content-type', '--text', '--transcript',
|
|
666
|
-
'--thread', '--
|
|
666
|
+
'--thread', '--title', '--description', '--text-from-file',
|
|
667
667
|
'--handoff-context', '--aun-path', '--app', '--format', '--encrypt', '--no-encrypt',
|
|
668
668
|
]);
|
|
669
669
|
/** Decode the EC inline text contract exactly once, after shell parsing. */
|
|
@@ -714,6 +714,77 @@ function decodePositionalText(argv, start, optionFlags) {
|
|
|
714
714
|
argv[index] = decodeEvolcoreInlineText(value);
|
|
715
715
|
}
|
|
716
716
|
}
|
|
717
|
+
// 取值为「逗号分隔、单项不含空格」的列表参数。aid、标签值、角色名里都不会有
|
|
718
|
+
// 空格,所以按空格拆分是安全的。
|
|
719
|
+
const MEMORY_LIST_FLAGS = new Set(['--tags', '--speakers']);
|
|
720
|
+
const SEND_LIST_FLAGS = new Set(['--mention']);
|
|
721
|
+
const FS_LIST_FLAGS = new Set(['--allow-roles']);
|
|
722
|
+
// 取值可能含空格的列表参数,只能按逗号拆:关键词是自由文本,记忆路径是 config
|
|
723
|
+
// 相对路径,两者都可能有空格。
|
|
724
|
+
const MEMORY_COMMA_ONLY_FLAGS = new Set(['--keywords', '--replaces']);
|
|
725
|
+
/** 逗号分隔项:去空、去首尾空白,重新用逗号连接。 */
|
|
726
|
+
function joinCommaList(value) {
|
|
727
|
+
return value.split(',').map(item => item.trim()).filter(Boolean).join(',');
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* 把列表参数统一成逗号分隔。PowerShell 把未加引号的 `a,b` 当数组,经 npm 的
|
|
731
|
+
* ec.ps1 用 `$args` 转发给 node 时逗号消失:`a,b` 变成 `a b`,`a, b` 和
|
|
732
|
+
* `a,b, c` 也变成 `a b` / `a b c`(实测 PS 5.1)。审批侧按命令串解析时看到的
|
|
733
|
+
* 是 `a,` 和 `b` 两个 token。两边在这里收敛到同一表示,委托哈希才能对上。
|
|
734
|
+
*
|
|
735
|
+
* 值以逗号结尾说明逗号数组还没结束:把后续 token 并进同一个值里再拆。值里本来
|
|
736
|
+
* 带逗号却没有空格的写法(bash 下 `--tags "a, b"`)也会被拆成两个标签,这是刻
|
|
737
|
+
* 意取舍——标签都是不含空格的单词,而 ec.ps1 已经把 `a, b` 和 `a,b` 归成了一
|
|
738
|
+
* 个值,无法再区分。
|
|
739
|
+
*/
|
|
740
|
+
function canonicalizeListFlags(argv, flags) {
|
|
741
|
+
for (let index = 0; index < argv.length; index++) {
|
|
742
|
+
const value = argv[index];
|
|
743
|
+
if (value === '--')
|
|
744
|
+
break;
|
|
745
|
+
if (!flags.has(value))
|
|
746
|
+
continue;
|
|
747
|
+
if (argv[index + 1] === undefined || argv[index + 1].startsWith('--'))
|
|
748
|
+
continue;
|
|
749
|
+
// `--tags a, b` 在 PowerShell 里是同一个值,审批侧却解析成 `a,` 加 `b`,
|
|
750
|
+
// 这里按逗号是否收尾把后续 token 续接回来
|
|
751
|
+
let end = index + 1;
|
|
752
|
+
while (argv[end].endsWith(',') && argv[end + 1] !== undefined
|
|
753
|
+
&& !argv[end + 1].startsWith('--')) {
|
|
754
|
+
end++;
|
|
755
|
+
}
|
|
756
|
+
argv[index + 1] = argv.slice(index + 1, end + 1)
|
|
757
|
+
.join(' ')
|
|
758
|
+
.split(/[,\s]+/u)
|
|
759
|
+
.filter(Boolean)
|
|
760
|
+
.join(',');
|
|
761
|
+
if (end > index + 1)
|
|
762
|
+
argv.splice(index + 2, end - index - 1);
|
|
763
|
+
index++;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* 只按逗号归一化,不动值里的空格。
|
|
768
|
+
*
|
|
769
|
+
* 与 canonicalizeListFlags 的区别:`--keywords 'a b'` 是一个含空格的关键词,
|
|
770
|
+
* 不能被拆成两个。所以只在值本身带逗号时整理(顺带去掉逗号周围的空白),
|
|
771
|
+
* `--keywords a,b` 这种未加引号的写法在 PowerShell 下会先被折成 `a b`,此处
|
|
772
|
+
* 认不出来——那属于被拒绝的写法,靠规范要求单引号避免。
|
|
773
|
+
*/
|
|
774
|
+
function canonicalizeCommaOnlyFlags(argv, flags) {
|
|
775
|
+
for (let index = 0; index < argv.length; index++) {
|
|
776
|
+
const value = argv[index];
|
|
777
|
+
if (value === '--')
|
|
778
|
+
break;
|
|
779
|
+
if (!flags.has(value))
|
|
780
|
+
continue;
|
|
781
|
+
const next = argv[index + 1];
|
|
782
|
+
if (next === undefined || next.startsWith('--') || !next.includes(','))
|
|
783
|
+
continue;
|
|
784
|
+
argv[index + 1] = joinCommaList(next);
|
|
785
|
+
index++;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
717
788
|
function canonicalizeHandoffText(argv) {
|
|
718
789
|
const action = argv[2];
|
|
719
790
|
if (action !== 'return' && action !== 'recover-return')
|
|
@@ -739,7 +810,8 @@ function canonicalizeHandoffText(argv) {
|
|
|
739
810
|
* Convert an EC invocation to the one argv representation shared by approval,
|
|
740
811
|
* delegation hashing, IPC, and command execution. Shell syntax is already
|
|
741
812
|
* resolved before this function is called; this function only normalizes the
|
|
742
|
-
* EC entrypoint
|
|
813
|
+
* EC entrypoint, applies the command-specific inline text contract, and joins
|
|
814
|
+
* memory list flags with commas.
|
|
743
815
|
*/
|
|
744
816
|
export function canonicalizeEvolcoreCommandArgv(argv) {
|
|
745
817
|
const canonical = normalizeEvolcoreCommandArgv(argv);
|
|
@@ -759,11 +831,13 @@ export function canonicalizeEvolcoreCommandArgv(argv) {
|
|
|
759
831
|
else if (command === 'msg' && subcommand === 'send') {
|
|
760
832
|
decodeInlineFlags(result, INLINE_TEXT_FLAGS.msgSend);
|
|
761
833
|
decodePositionalText(result, 5, MESSAGE_POSITIONAL_FLAGS);
|
|
834
|
+
canonicalizeListFlags(result, SEND_LIST_FLAGS);
|
|
762
835
|
}
|
|
763
836
|
else if (command === 'group') {
|
|
764
837
|
if (subcommand === 'send') {
|
|
765
838
|
decodeInlineFlags(result, INLINE_TEXT_FLAGS.groupSend);
|
|
766
839
|
decodePositionalText(result, 5, MESSAGE_POSITIONAL_FLAGS);
|
|
840
|
+
canonicalizeListFlags(result, SEND_LIST_FLAGS);
|
|
767
841
|
}
|
|
768
842
|
else if (subcommand === 'create')
|
|
769
843
|
decodeInlineFlags(result, INLINE_TEXT_FLAGS.groupCreate);
|
|
@@ -785,6 +859,13 @@ export function canonicalizeEvolcoreCommandArgv(argv) {
|
|
|
785
859
|
else if (command === 'handoff') {
|
|
786
860
|
canonicalizeHandoffText(result);
|
|
787
861
|
}
|
|
862
|
+
else if (command === 'memory') {
|
|
863
|
+
canonicalizeListFlags(result, MEMORY_LIST_FLAGS);
|
|
864
|
+
canonicalizeCommaOnlyFlags(result, MEMORY_COMMA_ONLY_FLAGS);
|
|
865
|
+
}
|
|
866
|
+
else if (command === 'fs' && subcommand === 'chmod') {
|
|
867
|
+
canonicalizeListFlags(result, FS_LIST_FLAGS);
|
|
868
|
+
}
|
|
788
869
|
return result;
|
|
789
870
|
}
|
|
790
871
|
const POWERSHELL_CREDENTIAL_ENV_RE = /^(EVOLCORE_CODEX_SESSION_TOKEN|EVOLCORE_DELEGATION_TOKEN)$/i;
|
|
@@ -1496,6 +1577,8 @@ export function parseLiteralShellCommand(command, options = {}) {
|
|
|
1496
1577
|
let token = '';
|
|
1497
1578
|
let tokenStarted = false;
|
|
1498
1579
|
let quote = null;
|
|
1580
|
+
// EC 命令的自由文本有额外的写法约束(见双引号分支的反引号/$ 转义处理)。
|
|
1581
|
+
const isEcCommand = hasEcCommandPrefix(input);
|
|
1499
1582
|
const finishToken = () => {
|
|
1500
1583
|
if (!tokenStarted)
|
|
1501
1584
|
return;
|
|
@@ -1527,6 +1610,12 @@ export function parseLiteralShellCommand(command, options = {}) {
|
|
|
1527
1610
|
return parseFailure(issue, input, index, argv.length, 'posix');
|
|
1528
1611
|
if (char === '\\') {
|
|
1529
1612
|
const next = input[++index];
|
|
1613
|
+
// EC 自由文本只接受单引号一种写法。双引号里用反斜杠转义 ` 或 $ 虽然
|
|
1614
|
+
// 能得到字面量,但同一条规则下要记两套写法,所以对 ec 命令显式拒绝,
|
|
1615
|
+
// 把正文一律导向单引号。非 ec 命令保持原行为。
|
|
1616
|
+
if (isEcCommand && (next === '`' || next === '$')) {
|
|
1617
|
+
return parseFailure('unsupported-escape', input, index, argv.length, 'posix');
|
|
1618
|
+
}
|
|
1530
1619
|
token += ['"', '\\', '$', '`'].includes(next) ? next : `\\${next}`;
|
|
1531
1620
|
continue;
|
|
1532
1621
|
}
|
|
@@ -1715,7 +1804,7 @@ function normalizeApplyPatchPath(value) {
|
|
|
1715
1804
|
return undefined;
|
|
1716
1805
|
return normalized;
|
|
1717
1806
|
}
|
|
1718
|
-
function parseApplyPatchDocument(source) {
|
|
1807
|
+
export function parseApplyPatchDocument(source) {
|
|
1719
1808
|
const lines = source.replace(/\r\n?/gu, '\n').split('\n');
|
|
1720
1809
|
while (lines.at(-1) === '')
|
|
1721
1810
|
lines.pop();
|
|
@@ -2762,7 +2851,8 @@ function findSingleFileOption(argv, startIndex) {
|
|
|
2762
2851
|
const PROACTIVE_SEND_VALUE_FLAGS = new Set([
|
|
2763
2852
|
'--format', '--app', '--file', '--text-from-file', '--link', '--payload',
|
|
2764
2853
|
'--title', '--description', '--text', '--transcript', '--as',
|
|
2765
|
-
'--content-type', '--mention', '--aun-path', '--thread', '--
|
|
2854
|
+
'--content-type', '--mention', '--aun-path', '--thread', '--handoff-context',
|
|
2855
|
+
'--check-every', '--trigger-prompt',
|
|
2766
2856
|
]);
|
|
2767
2857
|
const PROACTIVE_SEND_BOOLEAN_FLAGS = new Set([
|
|
2768
2858
|
'--encrypt', '--no-encrypt', '--mention-all',
|
|
@@ -2850,6 +2940,13 @@ function hasExecutableProactiveSendPayload(argv, scope) {
|
|
|
2850
2940
|
if (PROACTIVE_SEND_BOOLEAN_FLAGS.has(value))
|
|
2851
2941
|
continue;
|
|
2852
2942
|
if (!PROACTIVE_SEND_VALUE_FLAGS.has(value)) {
|
|
2943
|
+
// 未知 `--flag`(可能带值)不是正文;只有裸 token 计入正文。
|
|
2944
|
+
if (value.startsWith('--')) {
|
|
2945
|
+
const next = argv[index + 1];
|
|
2946
|
+
if (next !== undefined && !next.startsWith('--'))
|
|
2947
|
+
index++;
|
|
2948
|
+
continue;
|
|
2949
|
+
}
|
|
2853
2950
|
positionals.push(value);
|
|
2854
2951
|
continue;
|
|
2855
2952
|
}
|
|
@@ -2860,8 +2957,6 @@ function hasExecutableProactiveSendPayload(argv, scope) {
|
|
|
2860
2957
|
return false;
|
|
2861
2958
|
if (value === '--app' || value === '--aun-path')
|
|
2862
2959
|
return false;
|
|
2863
|
-
if (value === '--return' && operand !== 'none' && operand !== 'required')
|
|
2864
|
-
return false;
|
|
2865
2960
|
if (value === '--format' && operand !== 'json')
|
|
2866
2961
|
return false;
|
|
2867
2962
|
if (value === '--link') {
|
|
@@ -2970,8 +3065,12 @@ export function isExactEvolcoreSendCommandForSession(toolName, input, channelId,
|
|
|
2970
3065
|
&& !!argv
|
|
2971
3066
|
&& hasExecutableProactiveSendPayload(argv, expectedScope);
|
|
2972
3067
|
}
|
|
2973
|
-
/**
|
|
2974
|
-
|
|
3068
|
+
/**
|
|
3069
|
+
* 逐项诊断 proactive 发送命令被拒的具体原因。只读 argv 与(可选的)会话参数,
|
|
3070
|
+
* 不扩大任何授权面:授权判定仍在 isExactEvolcoreSendCommandForSession /
|
|
3071
|
+
* isThreadRoutedEvolcoreSendCommand 中。
|
|
3072
|
+
*/
|
|
3073
|
+
export function describeProactiveSendCommandFailure(toolName, input, session) {
|
|
2975
3074
|
if (!['Bash', 'Shell', 'exec'].includes(toolName) || !input || typeof input !== 'object')
|
|
2976
3075
|
return undefined;
|
|
2977
3076
|
const commandText = [input.command, input.input, ...(Array.isArray(input.commandArgv) ? input.commandArgv : [])]
|
|
@@ -2985,5 +3084,114 @@ export function describeProactiveSendCommandFailure(toolName, input) {
|
|
|
2985
3084
|
return `当前汇报命令未通过规范解析(${result.issue},${result.dialect},位置 ${result.offset});`
|
|
2986
3085
|
+ '请检查引号和转义,并将发送作为一条独立命令。';
|
|
2987
3086
|
}
|
|
2988
|
-
|
|
3087
|
+
// 正文里只是提到 "ec msg send" 的其它命令(如 ec memory note "...")不是发送调用。
|
|
3088
|
+
const argv = result.argv;
|
|
3089
|
+
if (argv[0] !== 'ec' || (argv[1] !== 'msg' && argv[1] !== 'group') || argv[2] !== 'send')
|
|
3090
|
+
return undefined;
|
|
3091
|
+
// --help / -h 是只读的参数查询,不是发送调用,更不该计入 proactive 违规计数。
|
|
3092
|
+
if (argv.includes('--help') || argv.includes('-h'))
|
|
3093
|
+
return undefined;
|
|
3094
|
+
const scope = argv[1];
|
|
3095
|
+
const prefix = '发送命令被拒:';
|
|
3096
|
+
const expectedForm = `ec ${scope} send ${session?.selfAid ?? '<selfAid>'} `
|
|
3097
|
+
+ (scope === 'group' ? `${session?.channelId ?? '<群AID>'}` : `${session?.channelId ?? '<对端AID>'}`)
|
|
3098
|
+
+ ' "<正文>"';
|
|
3099
|
+
// 带 --thread 的发送走话题路由校验,允许省略 from/to;走到这里说明话题本身没解析通过。
|
|
3100
|
+
if (sendOptionValue(argv, '--thread')) {
|
|
3101
|
+
return `${prefix}带 --thread 的跨话题发送未通过校验——话题必须已存在于本端索引,from(若给出)必须是本人,`
|
|
3102
|
+
+ `to(若给出)必须是该话题对端,且需携带 --check-every。可用 ec thread list <对方AID> 确认话题。`;
|
|
3103
|
+
}
|
|
3104
|
+
// 私聊/群聊用错了 scope。
|
|
3105
|
+
if (session?.chatType && (session.chatType === 'group') !== (scope === 'group')) {
|
|
3106
|
+
return session.chatType === 'group'
|
|
3107
|
+
? `${prefix}当前是群聊会话,公开回复必须用 ec group send ${session.selfAid ?? '<selfAid>'} `
|
|
3108
|
+
+ `${session.channelId ?? '<群AID>'} "<正文>";ec msg send 仅用于已获授权时私下联系本条消息发送者。`
|
|
3109
|
+
: `${prefix}当前是私聊会话,回复用 ${expectedForm};群回复才用 ec group send。`;
|
|
3110
|
+
}
|
|
3111
|
+
const from = argv[3];
|
|
3112
|
+
const to = argv[4];
|
|
3113
|
+
const fullFormHint = `${prefix}缺少发送者与目标。完整形式为 ${expectedForm};`
|
|
3114
|
+
+ `省略 from/to 仅在带 --thread 的跨话题发送中允许,普通回复也可用 ec ctl send '<正文>' 继承当前会话。`;
|
|
3115
|
+
if (!from || from.startsWith('--')) {
|
|
3116
|
+
return fullFormHint;
|
|
3117
|
+
}
|
|
3118
|
+
if (!to || to.startsWith('--')) {
|
|
3119
|
+
return looksLikeAid(from)
|
|
3120
|
+
? fullFormHint
|
|
3121
|
+
: `${prefix}缺少发送者与目标("${from}" 是正文而非 AID)。完整形式为 ${expectedForm};`
|
|
3122
|
+
+ `省略 from/to 仅在带 --thread 的跨话题发送中允许,普通回复也可用 ec ctl send '<正文>' 继承当前会话。`;
|
|
3123
|
+
}
|
|
3124
|
+
if (!looksLikeAid(from) || !looksLikeAid(to)) {
|
|
3125
|
+
return `${prefix}发送者/目标不是合法 AID(收到 from="${from}" to="${to}")。完整形式为 ${expectedForm}。`;
|
|
3126
|
+
}
|
|
3127
|
+
if (session?.selfAid && from !== session.selfAid) {
|
|
3128
|
+
return `${prefix}发送者必须是本会话身份 ${session.selfAid},命令里写的是 ${from}。完整形式为 ${expectedForm}。`;
|
|
3129
|
+
}
|
|
3130
|
+
if (session?.channelId && to !== session.channelId) {
|
|
3131
|
+
return `${prefix}目标必须是当前会话${scope === 'group' ? '群' : '对端'} ${session.channelId},命令里写的是 ${to};`
|
|
3132
|
+
+ `要联系其他对象请走 --thread 跨话题发送(ec thread list <对方AID> 选中话题后加 --check-every)。`;
|
|
3133
|
+
}
|
|
3134
|
+
return `${prefix}发送者与目标正确,但正文或参数无效——正文不能为空;--file/--text-from-file 属于文件发送,`
|
|
3135
|
+
+ '不在普通回复放行范围;--link 的值必须是 http(s) URL;--payload 的值必须是 JSON 对象。';
|
|
3136
|
+
}
|
|
3137
|
+
/** 与 CLI `isValidAid` 同一规则;parser 不依赖 AID 模块,避免引入身份存储副作用。 */
|
|
3138
|
+
function looksLikeAid(value) {
|
|
3139
|
+
if (!value)
|
|
3140
|
+
return false;
|
|
3141
|
+
const labels = value.split('.');
|
|
3142
|
+
return labels.length >= 3 && labels.every(label => /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(label));
|
|
3143
|
+
}
|
|
3144
|
+
function sendOptionValue(argv, flag) {
|
|
3145
|
+
for (let index = 3; index < argv.length; index++) {
|
|
3146
|
+
if (argv[index] === '--')
|
|
3147
|
+
return undefined;
|
|
3148
|
+
if (argv[index] === flag) {
|
|
3149
|
+
const value = argv[index + 1];
|
|
3150
|
+
return value && !value.startsWith('--') ? value : undefined;
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
return undefined;
|
|
3154
|
+
}
|
|
3155
|
+
/**
|
|
3156
|
+
* 带 `--thread` 的跨话题发送:目标由话题推断,不受「只能发给当前对端/群」限制。
|
|
3157
|
+
*
|
|
3158
|
+
* 支持完整形式 `ec msg|group send <self> <target> ... --thread <id>`,以及私聊
|
|
3159
|
+
* 简化形式 `ec msg send "<text>" --thread <id>`(目标取话题 peer)。
|
|
3160
|
+
* `resolveThread` 必须校验话题存在于本端索引、scope 与话题类型一致,且给出
|
|
3161
|
+
* target 时与话题 peer 相同;这里只做语法与发送者校验。
|
|
3162
|
+
*/
|
|
3163
|
+
export function isThreadRoutedEvolcoreSendCommand(toolName, input, selfAid, resolveThread) {
|
|
3164
|
+
if (!selfAid || !['Bash', 'Shell', 'exec'].includes(toolName) || !input || typeof input !== 'object')
|
|
3165
|
+
return false;
|
|
3166
|
+
const result = parseProactiveToolCommand(toolName, input);
|
|
3167
|
+
if (!result?.ok)
|
|
3168
|
+
return false;
|
|
3169
|
+
const argv = result.argv;
|
|
3170
|
+
if (argv[0] !== 'ec' || (argv[1] !== 'msg' && argv[1] !== 'group') || argv[2] !== 'send')
|
|
3171
|
+
return false;
|
|
3172
|
+
const scope = argv[1];
|
|
3173
|
+
const threadId = sendOptionValue(argv, '--thread');
|
|
3174
|
+
if (!threadId)
|
|
3175
|
+
return false;
|
|
3176
|
+
const self = selfAid.replace(/^@/, '');
|
|
3177
|
+
const hasFrom = looksLikeAid(argv[3]);
|
|
3178
|
+
const hasTo = hasFrom && looksLikeAid(argv[4]);
|
|
3179
|
+
let normalized;
|
|
3180
|
+
let target;
|
|
3181
|
+
if (hasFrom && hasTo) {
|
|
3182
|
+
if (argv[3] !== self)
|
|
3183
|
+
return false;
|
|
3184
|
+
target = argv[4];
|
|
3185
|
+
normalized = argv;
|
|
3186
|
+
}
|
|
3187
|
+
else if (!hasFrom && scope === 'msg') {
|
|
3188
|
+
// 与 CLI 托管推断一致:from/to 均省略时由 selfAid 与话题 peer 补齐。
|
|
3189
|
+
normalized = ['ec', scope, 'send', self, '<thread-peer>', ...argv.slice(3)];
|
|
3190
|
+
}
|
|
3191
|
+
else {
|
|
3192
|
+
return false;
|
|
3193
|
+
}
|
|
3194
|
+
if (!hasExecutableProactiveSendPayload(normalized, scope))
|
|
3195
|
+
return false;
|
|
3196
|
+
return resolveThread(scope, threadId, target);
|
|
2989
3197
|
}
|
|
@@ -938,7 +938,11 @@ export function lClassGrantIncludesProtectedPath(value, options = {}) {
|
|
|
938
938
|
// A grant covering an Agent root also covers its L-class configuration.
|
|
939
939
|
if (segments.length <= 2)
|
|
940
940
|
return true;
|
|
941
|
-
|
|
941
|
+
// relations/ 下的 L 类只有 relations/<peer>/config.json:只有范围是 relations/ 或
|
|
942
|
+
// relations/<peer> 本身时才覆盖它,更深的路径(如 relations/<peer>/memory/…)不会。
|
|
943
|
+
if (segments[2] === 'relations')
|
|
944
|
+
return segments.length <= 4;
|
|
945
|
+
return ['config.json', 'contact.json', 'contact-operations.jsonl', 'roles', 'triggers']
|
|
942
946
|
.includes(segments[2]);
|
|
943
947
|
}
|
|
944
948
|
return false;
|