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
package/dist/cli/aun-commands.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import crypto from 'crypto';
|
|
3
4
|
import { agentMdPath, resolvePaths, resolveRoot } from '../paths.js';
|
|
4
5
|
import { isExplicitGroupId, normalizeGroupAid, sameGroupAid } from '../aun/group/index.js';
|
|
5
6
|
import { getArgValue, isHelpFlag, wantsHelp } from './help.js';
|
|
@@ -11,6 +12,7 @@ import { readBestTaskRuntimeContext } from './task-context.js';
|
|
|
11
12
|
import { assertEcSingleLine } from './cli-argv.js';
|
|
12
13
|
import { canonicalizeEvolcoreCommandArgv } from '../core/permission/ec-command-parser.js';
|
|
13
14
|
import { ManagedGroupOperationError } from '../aun/msg/managed-operation.js';
|
|
15
|
+
import { parseDuration } from '../trigger/parser.js';
|
|
14
16
|
// ==================== AID ====================
|
|
15
17
|
function resolveAunPath(args) {
|
|
16
18
|
const idx = args.indexOf('--aun-path');
|
|
@@ -1307,6 +1309,176 @@ Options:
|
|
|
1307
1309
|
process.exit(1);
|
|
1308
1310
|
}
|
|
1309
1311
|
// ==================== Msg ====================
|
|
1312
|
+
/**
|
|
1313
|
+
* 构造跨话题复查 Trigger 的定义。
|
|
1314
|
+
*
|
|
1315
|
+
* `ec msg send --thread T2 --check-every 5m` 发送成功后自动创建。
|
|
1316
|
+
*
|
|
1317
|
+
* 使用 `interval` 而非 `delay`:周期触发,只有显式
|
|
1318
|
+
* `ec trigger cancel` 或 `ec trigger update --every <新时长>` 才改变其状态。
|
|
1319
|
+
* 一次性 Trigger 会静默消失,无法承担「一直跟进直到显式结束」的语义。
|
|
1320
|
+
*/
|
|
1321
|
+
export function buildThreadCheckTrigger(input) {
|
|
1322
|
+
const now = Date.now();
|
|
1323
|
+
return {
|
|
1324
|
+
$schema_version: 3.1,
|
|
1325
|
+
agentAid: input.agentAid,
|
|
1326
|
+
id: input.id,
|
|
1327
|
+
name: input.name,
|
|
1328
|
+
enabled: true,
|
|
1329
|
+
createdAt: now,
|
|
1330
|
+
updatedAt: now,
|
|
1331
|
+
// T10: labels 携带 kind + 业务上下文,渲染层按 kind=thread-check 路由到专属模板
|
|
1332
|
+
labels: {
|
|
1333
|
+
kind: 'thread-check',
|
|
1334
|
+
originThreadId: input.originThreadId,
|
|
1335
|
+
targetThreadId: input.targetThreadId,
|
|
1336
|
+
originPeer: input.originPeer,
|
|
1337
|
+
checkEvery: input.checkEvery,
|
|
1338
|
+
},
|
|
1339
|
+
...(input.origin ? { origin: input.origin } : {}),
|
|
1340
|
+
// 周期触发:每 everyMs 唤醒一次,直到显式取消/调整
|
|
1341
|
+
source: { type: 'interval', everyMs: input.everyMs },
|
|
1342
|
+
execution: {
|
|
1343
|
+
type: 'trigger_session',
|
|
1344
|
+
prompt: input.prompt,
|
|
1345
|
+
thread: 'by_trigger',
|
|
1346
|
+
onError: 'retry',
|
|
1347
|
+
noopSentinel: '[[NOOP]]',
|
|
1348
|
+
},
|
|
1349
|
+
feedback: {
|
|
1350
|
+
strategy: 'target',
|
|
1351
|
+
target: {
|
|
1352
|
+
channelKey: input.channelKey,
|
|
1353
|
+
channelId: input.targetChannelId,
|
|
1354
|
+
session: 'thread',
|
|
1355
|
+
threadId: input.targetThreadId,
|
|
1356
|
+
chatType: input.feedbackChatType ?? 'private',
|
|
1357
|
+
},
|
|
1358
|
+
},
|
|
1359
|
+
reliability: { concurrency: 'forbid', missedPolicy: 'run_once', retry: { maxAttempts: 0, backoffMs: 30_000 } },
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
/**
|
|
1363
|
+
* 解析并校验 `--check-every`。返回 `{ durationMs }` 或错误。
|
|
1364
|
+
* 私聊与群聊两条跨话题发送路径共用,保证错误码一致。
|
|
1365
|
+
*/
|
|
1366
|
+
function parseCheckEveryArgs(checkEveryRaw, hasCheckEvery) {
|
|
1367
|
+
if (!hasCheckEvery) {
|
|
1368
|
+
return { code: 'CHECK_EVERY_REQUIRED', error: '跨话题发送必须指定 --check-every <时长>(如 5m / 30m / 2h)' };
|
|
1369
|
+
}
|
|
1370
|
+
if (checkEveryRaw === undefined)
|
|
1371
|
+
return { durationMs: 5 * 60 * 1000 };
|
|
1372
|
+
const parsed = parseDuration(checkEveryRaw);
|
|
1373
|
+
if (parsed === null) {
|
|
1374
|
+
return { code: 'INVALID_DURATION', error: `--check-every 时长格式无效: ${checkEveryRaw}(示例: 5m / 30m / 2h / 1d)` };
|
|
1375
|
+
}
|
|
1376
|
+
return { durationMs: parsed };
|
|
1377
|
+
}
|
|
1378
|
+
/**
|
|
1379
|
+
* 跨话题发送成功后的统一收尾:创建周期复查 Trigger + 写入本端 thread-inbox。
|
|
1380
|
+
*
|
|
1381
|
+
* 私聊(`ec msg send --thread`)与群聊(`ec group send --thread`)共用,
|
|
1382
|
+
* 保证两条路径行为一致(M2 修复前群聊路径完全不做这两件事)。
|
|
1383
|
+
*
|
|
1384
|
+
* 设计见 `docs/thread-chain/thread-context-mechanism.md` §十、`thread-cli-spec-v2.md` §3.4。
|
|
1385
|
+
* 任意一步失败都不影响消息已发出的事实:返回 `triggerId` 为 undefined 时调用方照常输出发送结果。
|
|
1386
|
+
*/
|
|
1387
|
+
async function applyCrossThreadSendEffects(input) {
|
|
1388
|
+
const runtime = await readBestTaskRuntimeContext();
|
|
1389
|
+
const originThreadId = runtime?.threadId || 'main';
|
|
1390
|
+
const originPeer = runtime?.channelId || input.targetChannelId;
|
|
1391
|
+
const originSessionId = runtime?.sessionId;
|
|
1392
|
+
if (!originSessionId)
|
|
1393
|
+
return { inboxWritten: false };
|
|
1394
|
+
// Trigger 的 id 与 name 是两个不同的东西(见 src/trigger/validation.ts:727):
|
|
1395
|
+
// id = `trig_<ts>_<8位hex>` —— 唯一的持久化标识,`ec trigger show/update/cancel` 只认它
|
|
1396
|
+
// name = `check-<目标threadId>-from-<来源threadId>-<唯一后缀>` —— 人读标签,不参与定位
|
|
1397
|
+
//
|
|
1398
|
+
// 这里**在 IPC 之前**就把 id 定下来,有三个理由:
|
|
1399
|
+
// 1. prompt 里要写 `ec trigger cancel <id>`,而 prompt 是 definition 的一部分,
|
|
1400
|
+
// create 之前就得拼完,拿不到 daemon 的返回值;
|
|
1401
|
+
// 2. definition 带上 id 后 daemon 直接采用(normalizeV3Phase2 认 `raw.id`),
|
|
1402
|
+
// 落盘 id 与 prompt 中的 id 天然一致,不存在"返回值和 prompt 两套值"的漂移;
|
|
1403
|
+
// 3. D7:此前这里把拼出来的 name 当 id 用,导致 inbox 的 triggerId、`--format json`
|
|
1404
|
+
// 的 trigger_id、prompt 里的参考命令三处全部不可用(`ec trigger cancel` 报 not found)。
|
|
1405
|
+
const triggerSeed = `${Date.now()}_${crypto.randomBytes(4).toString('hex')}`;
|
|
1406
|
+
const triggerId = `trig_${triggerSeed}`;
|
|
1407
|
+
// D8:name 必须每次不同。此前用固定的 `check-<T>-from-<origin>`,同一
|
|
1408
|
+
// (目标, 来源) 对第二次发送就撞 `trigger name already exists`(assertUnique 按 name 去重),
|
|
1409
|
+
// 结果消息照发、inbox 照写、Trigger 静默不建 —— 完全无感知。
|
|
1410
|
+
// 每次 `--check-every` 都该有自己的 new Trigger(要复用应走 update),所以追加唯一后缀。
|
|
1411
|
+
const triggerName = `check-${input.targetThreadId}-from-${originThreadId}-${triggerSeed.split('_')[1]}`;
|
|
1412
|
+
const userPromptPart = input.triggerPromptExtra
|
|
1413
|
+
? input.triggerPromptExtra
|
|
1414
|
+
: `请检查该话题的最新进度,根据情况:继续等待、调整周期(ec trigger update ${triggerId} --every <时长>)或结束复查(ec trigger cancel ${triggerId})。`;
|
|
1415
|
+
const prompt = [
|
|
1416
|
+
`【Trigger 复查】话题 ${input.targetThreadId}(对端 ${input.targetChannelId})`,
|
|
1417
|
+
`这是周期复查:每 ${input.checkEveryLabel} 唤醒一次,直到你显式结束它。`,
|
|
1418
|
+
``,
|
|
1419
|
+
userPromptPart,
|
|
1420
|
+
``,
|
|
1421
|
+
`参考命令:`,
|
|
1422
|
+
` ec thread history ${input.targetChannelId}/${input.targetThreadId} --limit 10`,
|
|
1423
|
+
` ec thread history ${originPeer}/${originThreadId} --after ${Date.now()}`,
|
|
1424
|
+
` ec trigger cancel ${triggerId}`,
|
|
1425
|
+
` ec trigger update ${triggerId} --every <时长>`,
|
|
1426
|
+
`Trigger id:${triggerId}`,
|
|
1427
|
+
`Trigger 名称:${triggerName}`,
|
|
1428
|
+
].join('\n');
|
|
1429
|
+
let createdTriggerId;
|
|
1430
|
+
try {
|
|
1431
|
+
const { formatChannelKey } = await import('../core/channel-loader.js');
|
|
1432
|
+
const triggerDef = buildThreadCheckTrigger({
|
|
1433
|
+
agentAid: input.selfAid,
|
|
1434
|
+
id: triggerId,
|
|
1435
|
+
name: triggerName,
|
|
1436
|
+
prompt,
|
|
1437
|
+
everyMs: input.durationMs,
|
|
1438
|
+
checkEvery: input.checkEveryLabel,
|
|
1439
|
+
channelKey: formatChannelKey({ type: 'aun', selfAID: input.selfAid, name: 'main' }),
|
|
1440
|
+
targetChannelId: input.targetChannelId,
|
|
1441
|
+
targetThreadId: input.targetThreadId,
|
|
1442
|
+
originThreadId,
|
|
1443
|
+
originPeer,
|
|
1444
|
+
feedbackChatType: input.feedbackChatType,
|
|
1445
|
+
});
|
|
1446
|
+
const triggerRes = await ipcQuery(resolvePaths().socket, {
|
|
1447
|
+
type: 'trigger.create',
|
|
1448
|
+
definition: triggerDef,
|
|
1449
|
+
files: [],
|
|
1450
|
+
enable: true,
|
|
1451
|
+
// daemon 侧 authenticatedTriggerActor 先看 actorSessionId:缺了它会回落到
|
|
1452
|
+
// controlToken 分支(CLI 从不读 control.token),必然抛
|
|
1453
|
+
// "valid local control token is required"。与 trigger-command.ts 的组装保持一致。
|
|
1454
|
+
actorSessionId: originSessionId,
|
|
1455
|
+
delegationToken: readDelegationCredential(),
|
|
1456
|
+
}, 30_000);
|
|
1457
|
+
if (!triggerRes?.ok) {
|
|
1458
|
+
process.stderr.write(`⚠ Trigger 创建失败: ${triggerRes?.error ?? 'unknown'}(inbox 仍会写入)\n`);
|
|
1459
|
+
}
|
|
1460
|
+
else {
|
|
1461
|
+
// 以 daemon 落盘的结果为准(definition 已带 id,正常应与预设值相同;
|
|
1462
|
+
// 若 daemon 因任何原因改写了 id,回填真值而不是本地预设值)。
|
|
1463
|
+
createdTriggerId = triggerRes.trigger?.id ?? triggerId;
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
catch (e) {
|
|
1467
|
+
process.stderr.write(`⚠ Trigger 创建异常: ${e?.message ?? e}(inbox 仍会写入)\n`);
|
|
1468
|
+
}
|
|
1469
|
+
const { ThreadService } = await import('../core/thread/index.js');
|
|
1470
|
+
ThreadService.appendInbox(input.selfAid, {
|
|
1471
|
+
messageId: input.messageId,
|
|
1472
|
+
content: input.contentSummary,
|
|
1473
|
+
timestamp: input.timestamp,
|
|
1474
|
+
originThreadId,
|
|
1475
|
+
originPeer,
|
|
1476
|
+
originSessionId,
|
|
1477
|
+
targetSessionId: input.targetSessionId,
|
|
1478
|
+
...(createdTriggerId ? { triggerId: createdTriggerId } : {}),
|
|
1479
|
+
});
|
|
1480
|
+
return { triggerId: createdTriggerId, inboxWritten: true };
|
|
1481
|
+
}
|
|
1310
1482
|
export async function cmdMsg(args) {
|
|
1311
1483
|
args = canonicalizeEvolcoreCommandArgv(['ec', 'msg', ...args]).slice(2);
|
|
1312
1484
|
const sub = args[0];
|
|
@@ -1317,7 +1489,7 @@ export async function cmdMsg(args) {
|
|
|
1317
1489
|
console.log(`用法: ec msg <command> <from-aid> [args...] [options]
|
|
1318
1490
|
|
|
1319
1491
|
Commands:
|
|
1320
|
-
send <from> <to> <text>
|
|
1492
|
+
send <from> <to> <text> 发送文本
|
|
1321
1493
|
send <from> <to> --text-from-file <path> 从文件读取文本内容
|
|
1322
1494
|
send <from> <to> --file <path> [--as <type>] 发送文件(image|video|voice|file)
|
|
1323
1495
|
send <from> <to> --link <url> [--title T] 发送链接卡片
|
|
@@ -1328,6 +1500,10 @@ Commands:
|
|
|
1328
1500
|
online <from> <target-aid> [<target-aid>...] 查询在线状态
|
|
1329
1501
|
history <from> [<target-aid>|selector] [筛选选项] 查询本地消息历史
|
|
1330
1502
|
|
|
1503
|
+
托管会话简化语法(EVOLCORE_SESSION_ID 已设时):
|
|
1504
|
+
send <text> --thread <id> from/to 从运行时上下文与话题索引自动推断
|
|
1505
|
+
send <text> 发送到当前会话对端
|
|
1506
|
+
|
|
1331
1507
|
Options:
|
|
1332
1508
|
--text-from-file <path> 从文件读取文本(UTF-8),用于超长消息或避免 Shell 转义
|
|
1333
1509
|
--handoff-context <text> 仅托管会话:为目标会话补充本地上下文,不发给对端
|
|
@@ -1336,7 +1512,7 @@ Options:
|
|
|
1336
1512
|
--format json 输出 JSON 格式
|
|
1337
1513
|
--encrypt 强制端到端加密(未指定时使用 daemon 默认值)
|
|
1338
1514
|
--no-encrypt 强制明文发送(优先于 --encrypt)
|
|
1339
|
-
--thread <id>
|
|
1515
|
+
--thread <id> 指定目标话题 ID(话题必须存在于本端索引;from/to 可省略)
|
|
1340
1516
|
--session <latest|all|id> 按 EvolCore 逻辑会话过滤历史(默认 latest)
|
|
1341
1517
|
--before <time> 只返回该时间之前的历史
|
|
1342
1518
|
--after <time> 只返回该时间之后的历史
|
|
@@ -1345,7 +1521,10 @@ Options:
|
|
|
1345
1521
|
--channel-name <name> 历史渠道实例名(非 AUN 渠道必须匹配 Agent 配置)
|
|
1346
1522
|
--channel-key <key> 非 AUN 已落盘渠道的完整实例键(与 --peer-id 成对)
|
|
1347
1523
|
--peer-id <id> 非 AUN 渠道的原生对端 ID(与 --channel-key 成对)
|
|
1348
|
-
--
|
|
1524
|
+
--check-every <duration> 跨话题发送后自动创建**周期**复查 Trigger(如 5m/30m/2h),跨话题时**必填**
|
|
1525
|
+
每 duration 唤醒一次,直到显式 ec trigger cancel 结束
|
|
1526
|
+
Trigger 名:check-<目标threadId>-from-<来源threadId>-<唯一后缀>
|
|
1527
|
+
取消/调整用返回的 trigger_id(trig_…),show/update/cancel 只认 id
|
|
1349
1528
|
--content-type <mime> 显式覆盖 MIME(仅 --file 模式)
|
|
1350
1529
|
--as <type> 附件类型:image|video|voice|file(仅 --file 模式)
|
|
1351
1530
|
--text <说明> 附件说明文字(仅 --file 模式)
|
|
@@ -1356,12 +1535,12 @@ Options:
|
|
|
1356
1535
|
(用于发送恰好等于某 flag 的文本,如 send a b -- --encrypt)
|
|
1357
1536
|
|
|
1358
1537
|
示例:
|
|
1359
|
-
ec msg send alice.agentid.pub bob.agentid.pub "hello"
|
|
1360
|
-
ec msg send alice.agentid.pub bob.agentid.pub --text-from-file long-message.txt
|
|
1361
|
-
ec msg send alice.agentid.pub bob.agentid.pub "讨论项目A" --thread "
|
|
1362
|
-
ec msg send alice.agentid.pub bob.agentid.pub --file ./pic.png
|
|
1363
|
-
ec msg send alice.agentid.pub bob.agentid.pub --file ./demo.mp4 --as video
|
|
1364
|
-
ec msg send alice.agentid.pub bob.agentid.pub --link https://example.com --title "AUN"
|
|
1538
|
+
ec msg send alice.agentid.pub bob.agentid.pub "hello"
|
|
1539
|
+
ec msg send alice.agentid.pub bob.agentid.pub --text-from-file long-message.txt
|
|
1540
|
+
ec msg send alice.agentid.pub bob.agentid.pub "讨论项目A" --thread "thread-xxx"
|
|
1541
|
+
ec msg send alice.agentid.pub bob.agentid.pub --file ./pic.png
|
|
1542
|
+
ec msg send alice.agentid.pub bob.agentid.pub --file ./demo.mp4 --as video
|
|
1543
|
+
ec msg send alice.agentid.pub bob.agentid.pub --link https://example.com --title "AUN"
|
|
1365
1544
|
ec msg pull alice.agentid.pub --app my-bot
|
|
1366
1545
|
ec msg ack alice.agentid.pub 42 --app my-bot
|
|
1367
1546
|
ec msg recall alice.agentid.pub msg-uuid-1 msg-uuid-2
|
|
@@ -1371,13 +1550,16 @@ Options:
|
|
|
1371
1550
|
return;
|
|
1372
1551
|
}
|
|
1373
1552
|
if (sub === 'history' && wantsHelp(args)) {
|
|
1374
|
-
console.log(`用法: ec msg history <self-aid> [<target-aid>
|
|
1553
|
+
console.log(`用法: ec msg history [<self-aid> [<target-aid>]] [--thread <id>] [options]
|
|
1554
|
+
|
|
1555
|
+
托管会话中 --thread <id> 时 self-aid 和 target-aid 均可省略,从运行时上下文自动推断。
|
|
1375
1556
|
|
|
1376
1557
|
查询指定会话对的本地历史,不调用 message.pull,也不影响收件箱游标。非 AUN selector 按已落盘的 session metadata、完整 channelKey 与原生 peer ID 精确读取,不能传入文件路径。
|
|
1377
1558
|
|
|
1378
1559
|
托管会话限制:普通会话只能使用当前 self AID 和当前 AUN 私聊对端;可信 daemon Trigger 可用精确的 --channel-key/--peer-id selector 读取已落盘的非 AUN 私聊历史。托管任务不支持 --channel、--channel-name、--aun-path 或 --app。
|
|
1379
1560
|
|
|
1380
1561
|
Options:
|
|
1562
|
+
--thread <id> 按话题 ID 过滤(托管会话中 from/to 可省略)
|
|
1381
1563
|
--session <latest|all|id> 默认 latest;all 查询全部;也可指定会话 ID
|
|
1382
1564
|
--limit <N> 返回最新 N 条(默认 50,最大 500)
|
|
1383
1565
|
--before <time> 开区间;epoch 毫秒或 ISO 时间
|
|
@@ -1392,9 +1574,41 @@ Options:
|
|
|
1392
1574
|
ec msg history alice.agentid.pub bob.agentid.pub --session latest
|
|
1393
1575
|
ec msg history alice.agentid.pub bob.agentid.pub --session all
|
|
1394
1576
|
ec msg history alice.agentid.pub bob.agentid.pub --session meta_20260713_1783920000000 --format json
|
|
1395
|
-
ec msg history alice.agentid.pub --channel-key feishu#alice.agentid.pub#main --peer-id ou_xxx --session all --format json
|
|
1577
|
+
ec msg history alice.agentid.pub --channel-key feishu#alice.agentid.pub#main --peer-id ou_xxx --session all --format json
|
|
1578
|
+
ec msg history --thread thread-xxx-0001-aaaa --limit 20`);
|
|
1396
1579
|
return;
|
|
1397
1580
|
}
|
|
1581
|
+
// P1b.6.2: 托管会话 send 时,from/to 可省略——从 runtime 和话题索引推断
|
|
1582
|
+
// history --thread 时同理:from/to 均可省略
|
|
1583
|
+
if (process.env.EVOLCORE_SESSION_ID && (sub === 'send' || (sub === 'history' && getArgValue(args, '--thread')))) {
|
|
1584
|
+
const { isValidAid: checkAid } = await import('../aun/aid/index.js');
|
|
1585
|
+
const hasFrom = typeof args[1] === 'string' && checkAid(args[1]);
|
|
1586
|
+
const hasTo = typeof args[2] === 'string' && checkAid(args[2]);
|
|
1587
|
+
if (!hasFrom || !hasTo) {
|
|
1588
|
+
const runtime = await readBestTaskRuntimeContext();
|
|
1589
|
+
if (runtime?.selfAid) {
|
|
1590
|
+
const inferredFrom = runtime.selfAid.replace(/^@/, '');
|
|
1591
|
+
let inferredTo;
|
|
1592
|
+
const threadArg = getArgValue(args, '--thread');
|
|
1593
|
+
if (threadArg) {
|
|
1594
|
+
const { ThreadService } = await import('../core/thread/index.js');
|
|
1595
|
+
inferredTo = ThreadService.findThreadById(inferredFrom, threadArg)?.peer;
|
|
1596
|
+
}
|
|
1597
|
+
if (!inferredTo) {
|
|
1598
|
+
inferredTo = runtime.channelId?.replace(/^@/, '');
|
|
1599
|
+
}
|
|
1600
|
+
if (!hasFrom && !hasTo && inferredTo) {
|
|
1601
|
+
args.splice(1, 0, inferredFrom, inferredTo);
|
|
1602
|
+
}
|
|
1603
|
+
else if (!hasFrom && inferredTo) {
|
|
1604
|
+
args.splice(1, 0, inferredFrom);
|
|
1605
|
+
}
|
|
1606
|
+
else if (!hasTo && inferredTo) {
|
|
1607
|
+
args.splice(2, 0, inferredTo);
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1398
1612
|
const from = args[1];
|
|
1399
1613
|
if (!from) {
|
|
1400
1614
|
console.error('❌ 缺少 <from-aid> 参数');
|
|
@@ -1517,6 +1731,7 @@ Options:
|
|
|
1517
1731
|
before,
|
|
1518
1732
|
after,
|
|
1519
1733
|
direction: (getArgValue(args, '--direction') ?? 'all'),
|
|
1734
|
+
threadId: getArgValue(args, '--thread'),
|
|
1520
1735
|
});
|
|
1521
1736
|
if (!result.ok) {
|
|
1522
1737
|
if (format === 'json')
|
|
@@ -1626,11 +1841,38 @@ Options:
|
|
|
1626
1841
|
? false
|
|
1627
1842
|
: args.includes('--encrypt') ? true : undefined;
|
|
1628
1843
|
const thread = getArgValue(args, '--thread');
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1844
|
+
// P1b.6.5: 话题存在性校验 — --thread 指定的话题必须在本端索引里存在
|
|
1845
|
+
let threadEntry;
|
|
1846
|
+
if (thread) {
|
|
1847
|
+
const { ThreadService, normalizeThreadPeer } = await import('../core/thread/index.js');
|
|
1848
|
+
threadEntry = ThreadService.findThreadById(from, thread);
|
|
1849
|
+
if (!threadEntry || normalizeThreadPeer(threadEntry.peer) !== normalizeThreadPeer(to)) {
|
|
1850
|
+
if (formatJson) {
|
|
1851
|
+
console.log(JSON.stringify({ ok: false, code: 'TOPIC_NOT_FOUND', error: `话题不存在: ${thread}` }));
|
|
1852
|
+
}
|
|
1853
|
+
else {
|
|
1854
|
+
console.error(`❌ 话题不存在: ${thread}`);
|
|
1855
|
+
}
|
|
1856
|
+
process.exit(1);
|
|
1857
|
+
}
|
|
1633
1858
|
}
|
|
1859
|
+
// T8: 跨话题发送必须显式指定复查周期(发送前校验,避免消息已发出才报错)
|
|
1860
|
+
const checkEveryRaw = getArgValue(args, '--check-every');
|
|
1861
|
+
const hasCheckEvery = args.includes('--check-every') || checkEveryRaw !== undefined;
|
|
1862
|
+
const triggerPromptExtra = getArgValue(args, '--trigger-prompt');
|
|
1863
|
+
let checkEveryDurationMs;
|
|
1864
|
+
if (thread) {
|
|
1865
|
+
const parsedCheck = parseCheckEveryArgs(checkEveryRaw, hasCheckEvery);
|
|
1866
|
+
if ('error' in parsedCheck) {
|
|
1867
|
+
if (formatJson)
|
|
1868
|
+
console.log(JSON.stringify({ ok: false, code: parsedCheck.code, error: parsedCheck.error }));
|
|
1869
|
+
else
|
|
1870
|
+
console.error(`❌ ${parsedCheck.error}`);
|
|
1871
|
+
process.exit(1);
|
|
1872
|
+
}
|
|
1873
|
+
checkEveryDurationMs = parsedCheck.durationMs;
|
|
1874
|
+
}
|
|
1875
|
+
// --return 参数已移除:跨会话 send 不再需要指定返回策略(内部默认 none)。
|
|
1634
1876
|
// SDK 尚未提供字节进度回调,只展示上传开始和成功完成。
|
|
1635
1877
|
const onUploadProgress = formatJson ? undefined : (info) => {
|
|
1636
1878
|
if (info.phase === 'upload')
|
|
@@ -1638,7 +1880,8 @@ Options:
|
|
|
1638
1880
|
else if (info.phase === 'done')
|
|
1639
1881
|
process.stderr.write(' ✓ upload complete\n');
|
|
1640
1882
|
};
|
|
1641
|
-
const
|
|
1883
|
+
const runtime = await readBestTaskRuntimeContext();
|
|
1884
|
+
const result = await msgSend({ from, to, body, encrypt, thread, handoffContext: getArgValue(args, '--handoff-context'), onUploadProgress, ...commonOpts });
|
|
1642
1885
|
if (!result.ok) {
|
|
1643
1886
|
if (formatJson) {
|
|
1644
1887
|
console.log(JSON.stringify(result));
|
|
@@ -1648,6 +1891,31 @@ Options:
|
|
|
1648
1891
|
}
|
|
1649
1892
|
process.exit(1);
|
|
1650
1893
|
}
|
|
1894
|
+
// T2: 跨话题 send 成功后创建复查 Trigger 并写入本端 inbox(Thread Context)。
|
|
1895
|
+
// 私有逻辑集中在 applyCrossThreadSendEffects,与群聊路径共用。
|
|
1896
|
+
let createdTriggerId;
|
|
1897
|
+
if (thread && threadEntry && result.message_id && runtime?.sessionId) {
|
|
1898
|
+
const effects = await applyCrossThreadSendEffects({
|
|
1899
|
+
selfAid: from,
|
|
1900
|
+
targetThreadId: thread,
|
|
1901
|
+
targetSessionId: threadEntry.sessionId,
|
|
1902
|
+
targetChannelId: to,
|
|
1903
|
+
feedbackChatType: 'private',
|
|
1904
|
+
messageId: result.message_id,
|
|
1905
|
+
contentSummary: body.mode === 'text' ? body.text
|
|
1906
|
+
: body.mode === 'link' ? `[link] ${body.url}`
|
|
1907
|
+
: body.mode === 'file' ? `[file] ${body.filePath}`
|
|
1908
|
+
: '[payload]',
|
|
1909
|
+
timestamp: result.timestamp ?? Date.now(),
|
|
1910
|
+
durationMs: checkEveryDurationMs ?? 5 * 60 * 1000,
|
|
1911
|
+
checkEveryLabel: checkEveryRaw || '5m',
|
|
1912
|
+
triggerPromptExtra,
|
|
1913
|
+
});
|
|
1914
|
+
createdTriggerId = effects.triggerId;
|
|
1915
|
+
}
|
|
1916
|
+
// E4: 把复查 Trigger ID 回填到返回值,兑现 spec §3.4 的 `{ message_id, trigger_id }` 契约。
|
|
1917
|
+
if (createdTriggerId)
|
|
1918
|
+
result.trigger_id = createdTriggerId;
|
|
1651
1919
|
if (formatJson) {
|
|
1652
1920
|
console.log(JSON.stringify(result));
|
|
1653
1921
|
}
|
|
@@ -1850,7 +2118,7 @@ visitor 不开放历史;群权限仍由托管会话校验。`);
|
|
|
1850
2118
|
console.log(`用法: ec group <command> <from-aid> [args...] [options]
|
|
1851
2119
|
|
|
1852
2120
|
消息:
|
|
1853
|
-
send <from> <group-id> <text>
|
|
2121
|
+
send <from> <group-id> <text> 发送群文本
|
|
1854
2122
|
send <from> <group-id> --file <path> [--as <type>] 发送群文件
|
|
1855
2123
|
send <from> <group-id> --payload <json> 发送自定义 payload
|
|
1856
2124
|
pull <from> <group-id> [--after-seq N] [--limit N] --app <name> 拉取群消息(必须使用独立 slot)
|
|
@@ -1900,8 +2168,11 @@ Options:
|
|
|
1900
2168
|
--format json 输出 JSON 格式
|
|
1901
2169
|
--encrypt 强制端到端加密(未指定时使用 daemon 默认值;仅 send)
|
|
1902
2170
|
--no-encrypt 强制明文发送(优先于 --encrypt;仅 send)
|
|
1903
|
-
--thread <id> 指定话题 ID(仅 send
|
|
1904
|
-
--
|
|
2171
|
+
--thread <id> 指定话题 ID(仅 send;话题必须存在于本端索引)
|
|
2172
|
+
--check-every <时长> 跨话题发送(带 --thread)时**必填**,创建周期复查 Trigger(如 5m/30m/2h)
|
|
2173
|
+
无值时默认 5m;Trigger 名:check-<目标threadId>-from-<来源threadId>-<唯一后缀>
|
|
2174
|
+
取消/调整用返回的 trigger_id(trig_…),show/update/cancel 只认 id
|
|
2175
|
+
--trigger-prompt <text> 自定义复查 Trigger prompt 中的用户段(仅 --check-every)
|
|
1905
2176
|
--mention <aid> 发送时 @ 某个成员(可多次,或用逗号分隔多个 aid)
|
|
1906
2177
|
--mention-all 发送时 @ 所有人
|
|
1907
2178
|
--content-type <mime> 显式覆盖 MIME(仅 --file 模式)
|
|
@@ -1915,9 +2186,10 @@ Options:
|
|
|
1915
2186
|
|
|
1916
2187
|
格式示例(.example.invalid 是保留的虚构域,以下 AID 均不是真实地址):
|
|
1917
2188
|
ec group create sender.example.invalid "Dev Team" --visibility private
|
|
1918
|
-
ec group send sender.example.invalid 12345.example.invalid "hello team"
|
|
2189
|
+
ec group send sender.example.invalid 12345.example.invalid "hello team"
|
|
1919
2190
|
ec group send sender.example.invalid 12345.example.invalid "@member 看下 PR" --mention member.example.invalid
|
|
1920
2191
|
ec group send sender.example.invalid 12345.example.invalid --file ./arch.png
|
|
2192
|
+
ec group send sender.example.invalid g-12345.example.invalid "进度?" --thread thread-xxx --check-every 30m
|
|
1921
2193
|
ec group rules sender.example.invalid 12345.example.invalid set ./rules.md
|
|
1922
2194
|
ec group invite sender.example.invalid 12345.example.invalid member.example.invalid
|
|
1923
2195
|
ec group members sender.example.invalid 12345.example.invalid
|
|
@@ -2157,7 +2429,16 @@ g- 是名称的一部分,会完整保留;默认命名格式为 g-10000 至 g
|
|
|
2157
2429
|
failManagedGroup(formatJson, 'NOT_ALLOWED', '--aun-path and --app are not allowed in a managed task');
|
|
2158
2430
|
}
|
|
2159
2431
|
const runtime = await readBestTaskRuntimeContext();
|
|
2160
|
-
|
|
2432
|
+
// 跨话题发送:目标群由 --thread 话题决定,不受「只能操作当前群」限制;
|
|
2433
|
+
// 发送者仍必须是当前 self AID,daemon 侧按目标话题 relation 复核。
|
|
2434
|
+
const threadArg = getArgValue(args, '--thread');
|
|
2435
|
+
const { resolveThreadRouteTarget } = await import('../core/thread/thread-route.js');
|
|
2436
|
+
if (threadArg && resolveThreadRouteTarget(from, threadArg, 'group', groupId)) {
|
|
2437
|
+
validateManagedGroupScope(from, undefined, runtime, formatJson);
|
|
2438
|
+
}
|
|
2439
|
+
else {
|
|
2440
|
+
validateManagedGroupScope(from, groupId, runtime, formatJson);
|
|
2441
|
+
}
|
|
2161
2442
|
}
|
|
2162
2443
|
const optionsEnd = args.indexOf('--', 3);
|
|
2163
2444
|
const optionArgs = optionsEnd >= 0 ? args.slice(0, optionsEnd) : args;
|
|
@@ -2165,7 +2446,7 @@ g- 是名称的一部分,会完整保留;默认命名格式为 g-10000 至 g
|
|
|
2165
2446
|
const fileVal = getSendArgValue('--file');
|
|
2166
2447
|
const payloadVal = getSendArgValue('--payload');
|
|
2167
2448
|
const bodyPositionals = collectPositional(args, 3);
|
|
2168
|
-
const bodyFlags = ['--file', '--payload', '--as', '--content-type', '--text', '--transcript', '--thread', '--
|
|
2449
|
+
const bodyFlags = ['--file', '--payload', '--as', '--content-type', '--text', '--transcript', '--thread', '--check-every', '--trigger-prompt'];
|
|
2169
2450
|
for (let index = 3; index < args.length; index++) {
|
|
2170
2451
|
const flag = args[index];
|
|
2171
2452
|
if (flag === '--')
|
|
@@ -2238,9 +2519,24 @@ g- 是名称的一部分,会完整保留;默认命名格式为 g-10000 至 g
|
|
|
2238
2519
|
? false
|
|
2239
2520
|
: args.includes('--encrypt') ? true : undefined;
|
|
2240
2521
|
const thread = getSendArgValue('--thread');
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2522
|
+
// M2: 群话题跨话题发送与私聊同样必须指定 --check-every,并同样创建复查 Trigger + 写 inbox。
|
|
2523
|
+
const groupCheckEveryRaw = getSendArgValue('--check-every');
|
|
2524
|
+
const groupHasCheckEvery = optionArgs.includes('--check-every') || groupCheckEveryRaw !== undefined;
|
|
2525
|
+
const groupTriggerPromptExtra = getSendArgValue('--trigger-prompt');
|
|
2526
|
+
let groupCheckEveryMs;
|
|
2527
|
+
let groupThreadEntry;
|
|
2528
|
+
if (thread) {
|
|
2529
|
+
// 话题必须存在于本端索引(与私聊路径一致,不自动创建)。
|
|
2530
|
+
const { ThreadService } = await import('../core/thread/index.js');
|
|
2531
|
+
groupThreadEntry = ThreadService.findThreadById(from, thread);
|
|
2532
|
+
if (!groupThreadEntry || normalizeGroupAid(groupThreadEntry.peer, from.slice(from.indexOf('.') + 1)) !== groupId) {
|
|
2533
|
+
failManagedGroup(formatJson, 'TOPIC_NOT_FOUND', `话题不存在: ${thread}`);
|
|
2534
|
+
}
|
|
2535
|
+
const parsedCheck = parseCheckEveryArgs(groupCheckEveryRaw, groupHasCheckEvery);
|
|
2536
|
+
if ('error' in parsedCheck) {
|
|
2537
|
+
failManagedGroup(formatJson, parsedCheck.code, parsedCheck.error);
|
|
2538
|
+
}
|
|
2539
|
+
groupCheckEveryMs = parsedCheck.durationMs;
|
|
2244
2540
|
}
|
|
2245
2541
|
const result = await groupSend({
|
|
2246
2542
|
from,
|
|
@@ -2249,9 +2545,33 @@ g- 是名称的一部分,会完整保留;默认命名格式为 g-10000 至 g
|
|
|
2249
2545
|
mentions: mentions.length ? mentions : undefined,
|
|
2250
2546
|
encrypt: encryptGroup,
|
|
2251
2547
|
thread,
|
|
2252
|
-
returnPolicy: returnPolicyRaw,
|
|
2253
2548
|
...commonOpts,
|
|
2254
2549
|
});
|
|
2550
|
+
// T2.6: 群聊跨话题发送成功后,同样写入本端 inbox(与私聊路径一致)
|
|
2551
|
+
if (result && result.ok !== false && thread && groupThreadEntry) {
|
|
2552
|
+
const r = result;
|
|
2553
|
+
const messageId = r.message_id ?? r.message?.message_id;
|
|
2554
|
+
if (messageId) {
|
|
2555
|
+
const effects = await applyCrossThreadSendEffects({
|
|
2556
|
+
selfAid: from,
|
|
2557
|
+
targetThreadId: thread,
|
|
2558
|
+
targetSessionId: groupThreadEntry.sessionId,
|
|
2559
|
+
targetChannelId: groupId,
|
|
2560
|
+
feedbackChatType: 'group',
|
|
2561
|
+
messageId: String(messageId),
|
|
2562
|
+
contentSummary: body.mode === 'text' ? body.text
|
|
2563
|
+
: body.mode === 'link' ? `[link] ${body.url}`
|
|
2564
|
+
: body.mode === 'file' ? `[file] ${body.filePath}`
|
|
2565
|
+
: '[payload]',
|
|
2566
|
+
timestamp: r.timestamp ?? Date.now(),
|
|
2567
|
+
durationMs: groupCheckEveryMs ?? 5 * 60 * 1000,
|
|
2568
|
+
checkEveryLabel: groupCheckEveryRaw || '5m',
|
|
2569
|
+
triggerPromptExtra: groupTriggerPromptExtra,
|
|
2570
|
+
});
|
|
2571
|
+
if (effects.triggerId)
|
|
2572
|
+
result.trigger_id = effects.triggerId;
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2255
2575
|
outputResult(result, () => {
|
|
2256
2576
|
const r = result;
|
|
2257
2577
|
if (r.handoff_id && r.status === 'delivered')
|
|
@@ -2798,7 +3118,7 @@ export function resolveManagedSendArgv(argv) {
|
|
|
2798
3118
|
const booleans = new Set();
|
|
2799
3119
|
const text = [];
|
|
2800
3120
|
const mentions = [];
|
|
2801
|
-
const valueFlags = new Set(['--format', '--file', '--payload', '--text-from-file', '--link', '--title', '--description', '--as', '--content-type', '--text', '--transcript', '--thread', '--return', '--handoff-context', '--mention']);
|
|
3121
|
+
const valueFlags = new Set(['--format', '--file', '--payload', '--text-from-file', '--link', '--title', '--description', '--as', '--content-type', '--text', '--transcript', '--thread', '--check-every', '--trigger-prompt', '--return', '--handoff-context', '--mention']);
|
|
2802
3122
|
const booleanFlags = new Set(['--encrypt', '--no-encrypt', '--mention-all']);
|
|
2803
3123
|
for (let index = 4; index < argv.length; index++) {
|
|
2804
3124
|
const flag = argv[index];
|
|
@@ -2840,6 +3160,20 @@ export function resolveManagedSendArgv(argv) {
|
|
|
2840
3160
|
const handoffContext = values.get('--handoff-context');
|
|
2841
3161
|
if (handoffContext !== undefined && (scope !== 'msg' || !handoffContext.trim() || handoffContext.length > 65536))
|
|
2842
3162
|
throw new Error('Invalid private handoff context');
|
|
3163
|
+
const thread = values.get('--thread');
|
|
3164
|
+
const checkEvery = values.get('--check-every');
|
|
3165
|
+
const triggerPromptExtra = values.get('--trigger-prompt');
|
|
3166
|
+
if (!thread && (checkEvery !== undefined || triggerPromptExtra !== undefined))
|
|
3167
|
+
throw new Error('Cross-thread options require --thread');
|
|
3168
|
+
let checkEveryMs;
|
|
3169
|
+
if (thread) {
|
|
3170
|
+
if (checkEvery === undefined)
|
|
3171
|
+
throw new Error('Cross-thread sends require --check-every');
|
|
3172
|
+
const parsedCheckEveryMs = parseDuration(checkEvery);
|
|
3173
|
+
if (parsedCheckEveryMs === null)
|
|
3174
|
+
throw new Error('Invalid --check-every duration');
|
|
3175
|
+
checkEveryMs = parsedCheckEveryMs;
|
|
3176
|
+
}
|
|
2843
3177
|
if (scope === 'msg' && (mentions.length || booleans.has('--mention-all')))
|
|
2844
3178
|
throw new Error('Mentions require a group send');
|
|
2845
3179
|
if (scope === 'group' && (values.has('--link') || values.has('--text-from-file')))
|
|
@@ -2878,7 +3212,7 @@ export function resolveManagedSendArgv(argv) {
|
|
|
2878
3212
|
mentions.push({ scope: 'all' });
|
|
2879
3213
|
return { aid, to, scope, payload, file, textFile: values.get('--text-from-file'),
|
|
2880
3214
|
encrypt: booleans.has('--no-encrypt') ? false : booleans.has('--encrypt') ? true : undefined,
|
|
2881
|
-
thread:
|
|
3215
|
+
thread, checkEvery, checkEveryMs: checkEveryMs ?? undefined, triggerPromptExtra, returnPolicy: returnPolicy, handoffContext,
|
|
2882
3216
|
mentions: mentions.length ? mentions : undefined };
|
|
2883
3217
|
}
|
|
2884
3218
|
/**
|
|
@@ -2900,7 +3234,9 @@ const VALUE_FLAGS = new Set([
|
|
|
2900
3234
|
'--as', '--content-type', '--mention', '--visibility', '--join-mode',
|
|
2901
3235
|
'--group-id', '--name', '--message', '--answer', '--page', '--size',
|
|
2902
3236
|
'--member-type',
|
|
2903
|
-
'--aun-path', '--thread', '--
|
|
3237
|
+
'--aun-path', '--thread', '--handoff-context',
|
|
3238
|
+
// 跨话题发送的复查参数:漏登记会让 collectPositional 把它们当正文(正文污染)
|
|
3239
|
+
'--check-every', '--trigger-prompt',
|
|
2904
3240
|
]);
|
|
2905
3241
|
const BOOLEAN_FLAGS = new Set([
|
|
2906
3242
|
'--encrypt', '--no-encrypt', '--mention-all', '--clear',
|
package/dist/cli/contact.js
CHANGED
|
@@ -11,8 +11,14 @@ export async function cmdContact(args) {
|
|
|
11
11
|
}
|
|
12
12
|
const json = getArgValue(args, '--format') === 'json';
|
|
13
13
|
if (process.env.EVOLCORE_SESSION_ID) {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
try {
|
|
15
|
+
const result = await runManaged(args, json);
|
|
16
|
+
output(result, json);
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
console.error(`❌ ${err.message ?? String(err)}`);
|
|
20
|
+
process.exitCode = 1;
|
|
21
|
+
}
|
|
16
22
|
return;
|
|
17
23
|
}
|
|
18
24
|
if (!args.includes('--self') && isListCommand(args) && !process.env.EVOLCORE_SELF_AID?.trim()) {
|