evolcore 0.0.32 → 0.0.33
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 +24 -0
- package/bin/install-codex-managed-hooks.mjs +124 -37
- package/dist/agents/claude-runner.js +29 -2
- package/dist/agents/codex/app-server-client.js +3 -0
- package/dist/agents/codex/diagnostics.js +76 -44
- package/dist/agents/codex-runner.js +23 -19
- package/dist/agents/ecagent-runner.js +5 -13
- 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 +28 -2
- package/dist/channels/daemon.js +257 -2
- package/dist/cli/agent-command.js +58 -24
- package/dist/cli/agent.js +1 -1
- package/dist/cli/aun-commands.js +365 -29
- package/dist/cli/contact.js +8 -2
- package/dist/cli/daemon-commands.js +81 -12
- package/dist/cli/debug-mode.js +154 -0
- package/dist/cli/handoff-command.js +3 -0
- package/dist/cli/index.js +81 -64
- package/dist/cli/init.js +2 -2
- package/dist/cli/managed-command-guard.js +5 -0
- package/dist/cli/memory-command.js +8 -1
- 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 +620 -0
- package/dist/cli/trigger-command.js +4 -2
- package/dist/config/builtin-roles.js +1 -0
- package/dist/config/config-field-policy.js +2 -3
- package/dist/config/config-manager.js +7 -2
- package/dist/config/resolved-config-op.js +3 -0
- package/dist/config-store.js +15 -0
- package/dist/core/auth/agent-delegation.js +25 -1
- package/dist/core/auth/debug-context.js +139 -0
- package/dist/core/auth/operation-authorizer.js +24 -1
- package/dist/core/auth/operation-catalog.js +33 -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/command-handler.js +60 -24
- package/dist/core/command/memory-slash.js +6 -2
- package/dist/core/command/menu-catalog.js +48 -1
- package/dist/core/command/menu-handler.js +92 -16
- package/dist/core/command/menu-protocol.js +24 -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 +193 -13
- package/dist/core/command/topic-slash.js +528 -0
- package/dist/core/evolagent-registry.js +20 -5
- package/dist/core/handoff/runtime.js +5 -11
- package/dist/core/handoff/store.js +5 -0
- package/dist/core/ipc-registration.js +139 -2
- package/dist/core/memory/consolidation-context.js +49 -16
- package/dist/core/memory/consolidation-ops-records.js +35 -0
- package/dist/core/memory/consolidation-queue-format.js +21 -0
- package/dist/core/memory/consolidation.js +174 -180
- package/dist/core/message/aun-ipc-sender.js +161 -6
- package/dist/core/message/im-renderer.js +149 -6
- package/dist/core/message/message-bridge.js +13 -3
- 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 +349 -67
- 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 +126 -7
- package/dist/core/session/session-idle-monitor.js +249 -22
- package/dist/core/session/session-manager.js +54 -9
- package/dist/core/skill/command-parser.js +13 -0
- package/dist/core/skill/index-generator.js +4 -1
- package/dist/core/skill/startup.js +41 -0
- package/dist/core/skill/switch-resolver.js +55 -29
- 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 +777 -0
- package/dist/core/thread/types.js +1 -0
- package/dist/eck/message-renderer.js +35 -0
- package/dist/index.js +8 -5
- package/dist/ipc.js +89 -10
- package/dist/paths.js +23 -0
- package/dist/response-system/engines/v1/proactive-flow.js +169 -80
- package/dist/trigger/authorization.js +57 -28
- package/dist/trigger/feedback.js +7 -1
- package/dist/trigger/parser.js +63 -1
- package/dist/trigger/validation.js +14 -0
- package/dist/utils/error-utils.js +22 -1
- package/kits/docs/context-assembly.md +1 -0
- package/kits/docs/evolcore/INDEX.md +1 -0
- package/kits/docs/evolcore/agent-create.md +39 -28
- package/kits/docs/evolcore/agent.md +19 -4
- package/kits/docs/evolcore/ec-capabilities.md +4 -0
- package/kits/docs/evolcore/group-collaboration.md +3 -5
- package/kits/docs/evolcore/group.md +3 -4
- package/kits/docs/evolcore/memory-consolidation.md +1 -1
- package/kits/docs/evolcore/menu-exec-schema-commands.md +2 -2
- package/kits/docs/evolcore/menu-protocol.md +7 -0
- package/kits/docs/evolcore/msg.md +5 -8
- package/kits/docs/evolcore/thread-chain.md +267 -0
- package/kits/docs/prompt-loading-architecture.md +1 -0
- package/kits/eck_manifest.json +61 -26
- package/kits/eck_message_manifest.json +169 -10
- package/kits/rules/05-venue.md +1 -1
- package/kits/rules/07-memory-and-thread.md +202 -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 +15 -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 +5 -0
- package/kits/templates/memory-consolidation/generalize.md +5 -0
- package/kits/templates/memory-consolidation/profile.md +5 -0
- package/kits/templates/memory-consolidation/promote.md +5 -0
- 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 +66 -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/reply.md +3 -2
- package/kits/templates/system-fragments/session.md +5 -14
- package/package.json +3 -1
- 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. */
|
|
@@ -2762,7 +2762,8 @@ function findSingleFileOption(argv, startIndex) {
|
|
|
2762
2762
|
const PROACTIVE_SEND_VALUE_FLAGS = new Set([
|
|
2763
2763
|
'--format', '--app', '--file', '--text-from-file', '--link', '--payload',
|
|
2764
2764
|
'--title', '--description', '--text', '--transcript', '--as',
|
|
2765
|
-
'--content-type', '--mention', '--aun-path', '--thread', '--
|
|
2765
|
+
'--content-type', '--mention', '--aun-path', '--thread', '--handoff-context',
|
|
2766
|
+
'--check-every', '--trigger-prompt',
|
|
2766
2767
|
]);
|
|
2767
2768
|
const PROACTIVE_SEND_BOOLEAN_FLAGS = new Set([
|
|
2768
2769
|
'--encrypt', '--no-encrypt', '--mention-all',
|
|
@@ -2850,6 +2851,13 @@ function hasExecutableProactiveSendPayload(argv, scope) {
|
|
|
2850
2851
|
if (PROACTIVE_SEND_BOOLEAN_FLAGS.has(value))
|
|
2851
2852
|
continue;
|
|
2852
2853
|
if (!PROACTIVE_SEND_VALUE_FLAGS.has(value)) {
|
|
2854
|
+
// 未知 `--flag`(可能带值)不是正文;只有裸 token 计入正文。
|
|
2855
|
+
if (value.startsWith('--')) {
|
|
2856
|
+
const next = argv[index + 1];
|
|
2857
|
+
if (next !== undefined && !next.startsWith('--'))
|
|
2858
|
+
index++;
|
|
2859
|
+
continue;
|
|
2860
|
+
}
|
|
2853
2861
|
positionals.push(value);
|
|
2854
2862
|
continue;
|
|
2855
2863
|
}
|
|
@@ -2860,8 +2868,6 @@ function hasExecutableProactiveSendPayload(argv, scope) {
|
|
|
2860
2868
|
return false;
|
|
2861
2869
|
if (value === '--app' || value === '--aun-path')
|
|
2862
2870
|
return false;
|
|
2863
|
-
if (value === '--return' && operand !== 'none' && operand !== 'required')
|
|
2864
|
-
return false;
|
|
2865
2871
|
if (value === '--format' && operand !== 'json')
|
|
2866
2872
|
return false;
|
|
2867
2873
|
if (value === '--link') {
|
|
@@ -2970,8 +2976,12 @@ export function isExactEvolcoreSendCommandForSession(toolName, input, channelId,
|
|
|
2970
2976
|
&& !!argv
|
|
2971
2977
|
&& hasExecutableProactiveSendPayload(argv, expectedScope);
|
|
2972
2978
|
}
|
|
2973
|
-
/**
|
|
2974
|
-
|
|
2979
|
+
/**
|
|
2980
|
+
* 逐项诊断 proactive 发送命令被拒的具体原因。只读 argv 与(可选的)会话参数,
|
|
2981
|
+
* 不扩大任何授权面:授权判定仍在 isExactEvolcoreSendCommandForSession /
|
|
2982
|
+
* isThreadRoutedEvolcoreSendCommand 中。
|
|
2983
|
+
*/
|
|
2984
|
+
export function describeProactiveSendCommandFailure(toolName, input, session) {
|
|
2975
2985
|
if (!['Bash', 'Shell', 'exec'].includes(toolName) || !input || typeof input !== 'object')
|
|
2976
2986
|
return undefined;
|
|
2977
2987
|
const commandText = [input.command, input.input, ...(Array.isArray(input.commandArgv) ? input.commandArgv : [])]
|
|
@@ -2985,5 +2995,114 @@ export function describeProactiveSendCommandFailure(toolName, input) {
|
|
|
2985
2995
|
return `当前汇报命令未通过规范解析(${result.issue},${result.dialect},位置 ${result.offset});`
|
|
2986
2996
|
+ '请检查引号和转义,并将发送作为一条独立命令。';
|
|
2987
2997
|
}
|
|
2988
|
-
|
|
2998
|
+
// 正文里只是提到 "ec msg send" 的其它命令(如 ec memory note "...")不是发送调用。
|
|
2999
|
+
const argv = result.argv;
|
|
3000
|
+
if (argv[0] !== 'ec' || (argv[1] !== 'msg' && argv[1] !== 'group') || argv[2] !== 'send')
|
|
3001
|
+
return undefined;
|
|
3002
|
+
// --help / -h 是只读的参数查询,不是发送调用,更不该计入 proactive 违规计数。
|
|
3003
|
+
if (argv.includes('--help') || argv.includes('-h'))
|
|
3004
|
+
return undefined;
|
|
3005
|
+
const scope = argv[1];
|
|
3006
|
+
const prefix = '发送命令被拒:';
|
|
3007
|
+
const expectedForm = `ec ${scope} send ${session?.selfAid ?? '<selfAid>'} `
|
|
3008
|
+
+ (scope === 'group' ? `${session?.channelId ?? '<群AID>'}` : `${session?.channelId ?? '<对端AID>'}`)
|
|
3009
|
+
+ ' "<正文>"';
|
|
3010
|
+
// 带 --thread 的发送走话题路由校验,允许省略 from/to;走到这里说明话题本身没解析通过。
|
|
3011
|
+
if (sendOptionValue(argv, '--thread')) {
|
|
3012
|
+
return `${prefix}带 --thread 的跨话题发送未通过校验——话题必须已存在于本端索引,from(若给出)必须是本人,`
|
|
3013
|
+
+ `to(若给出)必须是该话题对端,且需携带 --check-every。可用 ec thread list <对方AID> 确认话题。`;
|
|
3014
|
+
}
|
|
3015
|
+
// 私聊/群聊用错了 scope。
|
|
3016
|
+
if (session?.chatType && (session.chatType === 'group') !== (scope === 'group')) {
|
|
3017
|
+
return session.chatType === 'group'
|
|
3018
|
+
? `${prefix}当前是群聊会话,公开回复必须用 ec group send ${session.selfAid ?? '<selfAid>'} `
|
|
3019
|
+
+ `${session.channelId ?? '<群AID>'} "<正文>";ec msg send 仅用于已获授权时私下联系本条消息发送者。`
|
|
3020
|
+
: `${prefix}当前是私聊会话,回复用 ${expectedForm};群回复才用 ec group send。`;
|
|
3021
|
+
}
|
|
3022
|
+
const from = argv[3];
|
|
3023
|
+
const to = argv[4];
|
|
3024
|
+
const fullFormHint = `${prefix}缺少发送者与目标。完整形式为 ${expectedForm};`
|
|
3025
|
+
+ '省略 from/to 仅在带 --thread 的跨话题发送中允许,普通回复也可用 ec ctl send "<正文>" 继承当前会话。';
|
|
3026
|
+
if (!from || from.startsWith('--')) {
|
|
3027
|
+
return fullFormHint;
|
|
3028
|
+
}
|
|
3029
|
+
if (!to || to.startsWith('--')) {
|
|
3030
|
+
return looksLikeAid(from)
|
|
3031
|
+
? fullFormHint
|
|
3032
|
+
: `${prefix}缺少发送者与目标("${from}" 是正文而非 AID)。完整形式为 ${expectedForm};`
|
|
3033
|
+
+ '省略 from/to 仅在带 --thread 的跨话题发送中允许,普通回复也可用 ec ctl send "<正文>" 继承当前会话。';
|
|
3034
|
+
}
|
|
3035
|
+
if (!looksLikeAid(from) || !looksLikeAid(to)) {
|
|
3036
|
+
return `${prefix}发送者/目标不是合法 AID(收到 from="${from}" to="${to}")。完整形式为 ${expectedForm}。`;
|
|
3037
|
+
}
|
|
3038
|
+
if (session?.selfAid && from !== session.selfAid) {
|
|
3039
|
+
return `${prefix}发送者必须是本会话身份 ${session.selfAid},命令里写的是 ${from}。完整形式为 ${expectedForm}。`;
|
|
3040
|
+
}
|
|
3041
|
+
if (session?.channelId && to !== session.channelId) {
|
|
3042
|
+
return `${prefix}目标必须是当前会话${scope === 'group' ? '群' : '对端'} ${session.channelId},命令里写的是 ${to};`
|
|
3043
|
+
+ `要联系其他对象请走 --thread 跨话题发送(ec thread list <对方AID> 选中话题后加 --check-every)。`;
|
|
3044
|
+
}
|
|
3045
|
+
return `${prefix}发送者与目标正确,但正文或参数无效——正文不能为空;--file/--text-from-file 属于文件发送,`
|
|
3046
|
+
+ '不在普通回复放行范围;--link 的值必须是 http(s) URL;--payload 的值必须是 JSON 对象。';
|
|
3047
|
+
}
|
|
3048
|
+
/** 与 CLI `isValidAid` 同一规则;parser 不依赖 AID 模块,避免引入身份存储副作用。 */
|
|
3049
|
+
function looksLikeAid(value) {
|
|
3050
|
+
if (!value)
|
|
3051
|
+
return false;
|
|
3052
|
+
const labels = value.split('.');
|
|
3053
|
+
return labels.length >= 3 && labels.every(label => /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(label));
|
|
3054
|
+
}
|
|
3055
|
+
function sendOptionValue(argv, flag) {
|
|
3056
|
+
for (let index = 3; index < argv.length; index++) {
|
|
3057
|
+
if (argv[index] === '--')
|
|
3058
|
+
return undefined;
|
|
3059
|
+
if (argv[index] === flag) {
|
|
3060
|
+
const value = argv[index + 1];
|
|
3061
|
+
return value && !value.startsWith('--') ? value : undefined;
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
return undefined;
|
|
3065
|
+
}
|
|
3066
|
+
/**
|
|
3067
|
+
* 带 `--thread` 的跨话题发送:目标由话题推断,不受「只能发给当前对端/群」限制。
|
|
3068
|
+
*
|
|
3069
|
+
* 支持完整形式 `ec msg|group send <self> <target> ... --thread <id>`,以及私聊
|
|
3070
|
+
* 简化形式 `ec msg send "<text>" --thread <id>`(目标取话题 peer)。
|
|
3071
|
+
* `resolveThread` 必须校验话题存在于本端索引、scope 与话题类型一致,且给出
|
|
3072
|
+
* target 时与话题 peer 相同;这里只做语法与发送者校验。
|
|
3073
|
+
*/
|
|
3074
|
+
export function isThreadRoutedEvolcoreSendCommand(toolName, input, selfAid, resolveThread) {
|
|
3075
|
+
if (!selfAid || !['Bash', 'Shell', 'exec'].includes(toolName) || !input || typeof input !== 'object')
|
|
3076
|
+
return false;
|
|
3077
|
+
const result = parseProactiveToolCommand(toolName, input);
|
|
3078
|
+
if (!result?.ok)
|
|
3079
|
+
return false;
|
|
3080
|
+
const argv = result.argv;
|
|
3081
|
+
if (argv[0] !== 'ec' || (argv[1] !== 'msg' && argv[1] !== 'group') || argv[2] !== 'send')
|
|
3082
|
+
return false;
|
|
3083
|
+
const scope = argv[1];
|
|
3084
|
+
const threadId = sendOptionValue(argv, '--thread');
|
|
3085
|
+
if (!threadId)
|
|
3086
|
+
return false;
|
|
3087
|
+
const self = selfAid.replace(/^@/, '');
|
|
3088
|
+
const hasFrom = looksLikeAid(argv[3]);
|
|
3089
|
+
const hasTo = hasFrom && looksLikeAid(argv[4]);
|
|
3090
|
+
let normalized;
|
|
3091
|
+
let target;
|
|
3092
|
+
if (hasFrom && hasTo) {
|
|
3093
|
+
if (argv[3] !== self)
|
|
3094
|
+
return false;
|
|
3095
|
+
target = argv[4];
|
|
3096
|
+
normalized = argv;
|
|
3097
|
+
}
|
|
3098
|
+
else if (!hasFrom && scope === 'msg') {
|
|
3099
|
+
// 与 CLI 托管推断一致:from/to 均省略时由 selfAid 与话题 peer 补齐。
|
|
3100
|
+
normalized = ['ec', scope, 'send', self, '<thread-peer>', ...argv.slice(3)];
|
|
3101
|
+
}
|
|
3102
|
+
else {
|
|
3103
|
+
return false;
|
|
3104
|
+
}
|
|
3105
|
+
if (!hasExecutableProactiveSendPayload(normalized, scope))
|
|
3106
|
+
return false;
|
|
3107
|
+
return resolveThread(scope, threadId, target);
|
|
2989
3108
|
}
|