evolcore 0.0.35 → 0.0.36
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 +31 -0
- package/README.md +6 -0
- package/bin/install-codex-managed-hooks.mjs +20 -3
- package/dist/agents/baseagent-availability.js +1 -1
- package/dist/agents/baseagent.js +88 -0
- package/dist/agents/claude-runner.js +59 -12
- package/dist/agents/codex/app-server-client.js +18 -1
- package/dist/agents/codex-runner.js +262 -21
- package/dist/agents/ecagent-runner.js +47 -11
- package/dist/agents/gemini-runner.js +36 -12
- package/dist/aun/aid/client.js +4 -3
- package/dist/aun/aid/control-aid.js +270 -8
- package/dist/aun/aid/identity.js +6 -3
- package/dist/aun/aid/ownership-operation.js +58 -0
- package/dist/aun/aid/store.js +1 -0
- package/dist/aun/group/fs-download.js +67 -2
- package/dist/aun/group/history.js +20 -5
- package/dist/aun/group/operations.js +8 -2
- package/dist/aun/menu/client.js +39 -2
- package/dist/aun/menu/transport.js +1 -1
- package/dist/aun/msg/history.js +43 -4
- package/dist/aun/msg/managed-operation.js +30 -53
- package/dist/aun/msg/p2p.js +51 -15
- package/dist/aun/outbox.js +4 -0
- package/dist/channels/aun.js +260 -73
- package/dist/channels/daemon.js +6 -4
- package/dist/channels/feishu.js +3 -0
- package/dist/cli/agent-command.js +161 -223
- package/dist/cli/agent-create.js +190 -223
- package/dist/cli/agent.js +3 -1
- package/dist/cli/aid-bind-command.js +53 -0
- package/dist/cli/aun-commands.js +384 -91
- package/dist/cli/bench.js +1 -1
- package/dist/cli/cli-argv.js +74 -0
- package/dist/cli/daemon-commands.js +105 -74
- package/dist/cli/diag.js +1 -1
- package/dist/cli/fs-command.js +18 -19
- package/dist/cli/getmeta-command.js +159 -6
- package/dist/cli/index.js +6 -12
- package/dist/cli/init-channel.js +8 -55
- package/dist/cli/init.js +125 -218
- package/dist/cli/queue-command.js +11 -8
- package/dist/cli/restart-monitor.js +134 -48
- package/dist/cli/session-command.js +1 -1
- package/dist/cli/stats.js +7 -5
- package/dist/cli/thread-command.js +103 -30
- package/dist/cli/trigger-command.js +1 -1
- package/dist/cli/watch-msg-diag.js +1 -0
- package/dist/config/access-policy.js +2 -2
- package/dist/config/boot-log.js +14 -11
- package/dist/config/config-field-policy.js +9 -1
- package/dist/config/config-manager.js +176 -90
- package/dist/config/config-operation-service.js +7 -14
- package/dist/config/contact-book-store.js +142 -26
- package/dist/config/contact-book.js +4 -2
- package/dist/config/lifecycle.js +1 -1
- package/dist/config/owner-config.js +96 -0
- package/dist/config/peer-role-resolver.js +12 -18
- package/dist/config/providers-config.js +382 -0
- package/dist/config/providers-import.js +74 -0
- package/dist/config/providers-management.js +328 -0
- package/dist/config/providers.js +22 -0
- package/dist/config/resolved-config-op.js +17 -14
- package/dist/config/snapshot.js +28 -16
- package/dist/config-store.js +18 -14
- package/dist/core/agent-ipc-registration.js +21 -63
- package/dist/core/agent-runtime.js +16 -5
- package/dist/core/auth/agent-delegation.js +199 -25
- package/dist/core/auth/auth-gateway.js +44 -2
- package/dist/core/auth/authorization-denial.js +1 -1
- package/dist/core/auth/debug-context.js +2 -1
- package/dist/core/auth/delegation-authorization.js +2 -2
- package/dist/core/auth/delegation-diagnostics.js +4 -1
- package/dist/core/auth/operation-authorizer.js +10 -1
- package/dist/core/auth/operation-catalog.js +19 -1
- package/dist/core/bootstrap-service.js +43 -20
- package/dist/core/channel-loader.js +0 -1
- package/dist/core/channel-runtime.js +30 -4
- package/dist/core/command/agent-control.js +196 -37
- package/dist/core/command/cli-intent-parser.js +60 -4
- package/dist/core/command/command-handler.js +212 -60
- package/dist/core/command/connect-menu.js +1 -0
- package/dist/core/command/diag-service.js +5 -4
- package/dist/core/command/file-search.js +368 -0
- package/dist/core/command/menu-catalog.js +2 -1
- package/dist/core/command/menu-handler.js +292 -107
- package/dist/core/command/menu-protocol.js +23 -0
- package/dist/core/command/role-menu.js +7 -19
- package/dist/core/command/skill-slash.js +130 -25
- package/dist/core/command/slash-handler.js +101 -53
- package/dist/core/command/topic-slash.js +16 -5
- package/dist/core/daemon-bootstrap.js +7 -1
- package/dist/core/daemon-control.js +19 -1
- package/dist/core/daemon-lifecycle.js +2 -1
- package/dist/core/event-catalog.js +2 -0
- package/dist/core/evolagent-registry.js +13 -2
- package/dist/core/evolagent.js +3 -6
- package/dist/core/handoff/ipc-registration.js +2 -2
- package/dist/core/handoff/runtime.js +40 -6
- package/dist/core/handoff/store.js +1 -0
- package/dist/core/inference/text-inference.js +95 -6
- package/dist/core/ipc-registration.js +43 -24
- package/dist/core/memory/consolidation-context.js +4 -1
- package/dist/core/memory/consolidation.js +25 -14
- package/dist/core/message/aun-ipc-sender.js +296 -88
- package/dist/core/message/aun-request-registry.js +226 -0
- package/dist/core/message/create-status.js +33 -4
- package/dist/core/message/message-bridge.js +62 -13
- package/dist/core/message/message-log.js +0 -1
- package/dist/core/message/message-queue.js +4 -1
- package/dist/core/message/message-utils.js +8 -1
- package/dist/core/message/peer-mode.js +17 -9
- package/dist/core/message/response-engine.js +266 -124
- package/dist/core/message/retry-scheduler.js +11 -8
- package/dist/core/model/catalog-service.js +60 -18
- package/dist/core/model/model-catalog.js +112 -9
- package/dist/core/model/model-fallback.js +94 -26
- package/dist/core/ownership/owner-resolver.js +687 -0
- package/dist/core/permission/approval-gateway.js +196 -62
- package/dist/core/permission/ec-command-parser.js +138 -25
- package/dist/core/permission/filesystem-approval-scope.js +141 -0
- package/dist/core/permission/protected-paths.js +14 -1
- package/dist/core/permission/sandbox-runtime.js +171 -18
- package/dist/core/permission/tool-policy.js +13 -2
- package/dist/core/role/runtime-policy.js +3 -4
- package/dist/core/session/session-idle-monitor.js +10 -8
- package/dist/core/session/session-manager.js +72 -45
- package/dist/core/session/session-mapper.js +18 -7
- package/dist/core/session/session-renew.js +5 -4
- package/dist/core/session/session-title.js +2 -1
- package/dist/core/session/session-turn-coordinator.js +4 -2
- package/dist/core/skill/index-generator.js +2 -16
- package/dist/core/task/task-recovery-service.js +15 -2
- package/dist/core/thread/command-parser.js +28 -13
- package/dist/core/thread/index.js +1 -0
- package/dist/core/thread/ipc-operation.js +11 -8
- package/dist/core/thread/thread-operation.js +79 -44
- package/dist/core/thread/thread-route.js +54 -6
- package/dist/core/thread/thread-service.js +236 -45
- package/dist/core/thread/topic-id.js +248 -0
- package/dist/eck/kit-renderer.js +1 -1
- package/dist/eck/message-renderer.js +12 -2
- package/dist/index.js +50 -27
- package/dist/ipc.js +59 -6
- package/dist/paths.js +1 -0
- package/dist/product.js +1 -0
- package/dist/response-system/engines/v1/proactive-flow.js +10 -3
- package/dist/stats/db.js +8 -0
- package/dist/stats/writer.js +26 -4
- package/dist/trigger/authorization.js +48 -3
- package/dist/trigger/feedback.js +28 -10
- package/dist/trigger/ipc-service.js +86 -10
- package/dist/trigger/local-files.js +55 -23
- package/dist/trigger/scheduler.js +57 -11
- package/dist/trigger/system-definitions.js +1 -0
- package/dist/trigger/validation.js +44 -15
- package/dist/utils/aid-bind.js +17 -297
- package/dist/utils/avatar-upload.js +16 -4
- package/dist/utils/cross-platform.js +109 -34
- package/dist/utils/ecweb-supervisor.js +1 -1
- package/dist/utils/ecweb-utils.js +8 -1
- package/dist/utils/error-utils.js +44 -6
- package/dist/utils/instance-registry.js +12 -2
- package/dist/utils/npm-ops.js +102 -27
- package/dist/utils/package-version-info.js +72 -27
- package/dist/utils/restart-safety.js +50 -0
- package/dist/utils/upgrade-jobs.js +304 -0
- package/dist/utils/windows-autostart.js +17 -10
- package/kits/docs/INDEX.md +5 -0
- package/kits/docs/aun/ABILITY_CALLING.md +239 -0
- package/kits/docs/evolcore/INDEX.md +14 -3
- package/kits/docs/evolcore/agent-create.md +55 -141
- package/kits/docs/evolcore/agent.md +38 -16
- package/kits/docs/evolcore/aid.md +34 -0
- package/kits/docs/evolcore/config.md +20 -1
- package/kits/docs/evolcore/event.md +2 -2
- package/kits/docs/evolcore/fs.md +18 -2
- package/kits/docs/evolcore/group-collaboration.md +13 -246
- package/kits/docs/evolcore/group.md +2 -1
- package/kits/docs/evolcore/memory-consolidation-batches.md +1 -1
- package/kits/docs/evolcore/menu-protocol.md +65 -1
- package/kits/docs/evolcore/msg.md +64 -2
- package/kits/docs/evolcore/skill.md +86 -0
- package/kits/docs/evolcore/stats.md +6 -0
- package/kits/docs/evolcore/task.md +2 -0
- package/kits/docs/evolcore/thread-chain.md +145 -51
- package/kits/docs/evolcore/trigger.md +48 -3
- package/kits/eck_manifest.json +21 -0
- package/kits/eck_message_manifest.json +4 -4
- package/kits/migrations/migrate-role-config-v5.mjs +20 -7
- package/kits/rules/01-overview.md +2 -1
- package/kits/rules/03-identity.md +3 -1
- package/kits/rules/04-relation.md +1 -1
- package/kits/rules/07-memory-and-thread.md +1 -1
- package/kits/schemas/_meta.json +16 -1
- package/kits/schemas/agent-config.schema.13.json +44 -2
- package/kits/schemas/contact-book.schema.3.json +5 -0
- package/kits/schemas/daemon.schema.6.json +3 -1
- package/kits/schemas/daemon.schema.8.json +329 -0
- package/kits/schemas/defaults.schema.5.json +39 -3
- package/kits/schemas/providers.schema.1.json +76 -0
- package/kits/schemas/relation-config.schema.9.json +5 -1
- package/kits/skills/.switches.json +28 -0
- package/kits/skills/aid-ability/SKILL.md +214 -0
- package/kits/skills/ec-agent-create/SKILL.md +162 -0
- package/kits/skills/ec-group/SKILL.md +2 -1
- package/kits/skills/ec-group-collaboration/SKILL.md +228 -0
- package/kits/skills/ec-identity/SKILL.md +143 -0
- package/kits/skills/ec-skill/scripts/cli.mjs +36 -3
- package/kits/skills/ec-skill/scripts/skill.mjs +202 -15
- package/kits/skills/ec-trigger/SKILL.md +167 -0
- package/kits/skills/ec-trigger/references/examples.md +153 -0
- package/kits/skills/thread-ops/SKILL.md +108 -420
- package/kits/templates/message-fragments/thread-context.md +1 -2
- package/kits/templates/message-fragments/trigger-thread-check.md +1 -1
- package/kits/templates/roles/member.json +1 -1
- package/kits/templates/roles/visitor.json +1 -0
- package/kits/templates/system-fragments/bootstrap.md +1 -0
- package/kits/templates/system-fragments/channel.md +2 -1
- package/kits/templates/system-fragments/commands.md +5 -4
- package/kits/templates/system-fragments/identity.md +2 -0
- package/kits/templates/system-fragments/session.md +1 -1
- package/kits/templates/system-fragments/venue.md +1 -1
- package/package.json +4 -3
package/dist/cli/aun-commands.js
CHANGED
|
@@ -9,10 +9,12 @@ import { isValidAid as validSendAid } from '../aun/aid/validation.js';
|
|
|
9
9
|
import { AGENT_DELEGATION_TOKEN_ENV, readDelegationCredential } from '../core/auth/agent-delegation.js';
|
|
10
10
|
import { isManagementRole } from '../config/builtin-roles.js';
|
|
11
11
|
import { readBestTaskRuntimeContext } from './task-context.js';
|
|
12
|
-
import { assertEcSingleLine } from './cli-argv.js';
|
|
12
|
+
import { assertEcSingleLine, parseTimeArg, TIME_ARG_FORMATS } from './cli-argv.js';
|
|
13
13
|
import { canonicalizeEvolcoreCommandArgv } from '../core/permission/ec-command-parser.js';
|
|
14
14
|
import { ManagedGroupOperationError } from '../aun/msg/managed-operation.js';
|
|
15
15
|
import { parseDuration } from '../trigger/parser.js';
|
|
16
|
+
import { isTopicThreadId, safeFormatMainTopicId } from '../core/thread/topic-id.js';
|
|
17
|
+
import { parseJsonObjectArgument } from '../core/permission/ec-command-parser.js';
|
|
16
18
|
// ==================== AID ====================
|
|
17
19
|
function resolveAunPath(args) {
|
|
18
20
|
const idx = args.indexOf('--aun-path');
|
|
@@ -194,9 +196,9 @@ async function runManagedAid(args, formatJson) {
|
|
|
194
196
|
console.log(`✓ 头像已更新${response.result.avatar ? `: ${response.result.avatar}` : ''}${response.result.sha256 ? ` (sha256: ${response.result.sha256})` : ''}`);
|
|
195
197
|
}
|
|
196
198
|
}
|
|
197
|
-
function failManagedGroup(formatJson, code, error) {
|
|
199
|
+
function failManagedGroup(formatJson, code, error, details = {}) {
|
|
198
200
|
if (formatJson)
|
|
199
|
-
console.log(JSON.stringify({ ok: false, code, error }));
|
|
201
|
+
console.log(JSON.stringify({ ok: false, code, error, ...details }));
|
|
200
202
|
else
|
|
201
203
|
console.error(`❌ ${error}`);
|
|
202
204
|
process.exit(1);
|
|
@@ -465,6 +467,8 @@ Commands:
|
|
|
465
467
|
agentmd get <aid> 下载 agent.md → 验签 → 本地持久化
|
|
466
468
|
agentmd set <aid> <field> <value> 修改一个字段并签名上传
|
|
467
469
|
avatar <aid> --file <path> | --url <https-url> 上传头像并更新 agent.md
|
|
470
|
+
bind <ownerAid> [--subject <subjectAid>] 发起 Ownership 2.1 绑定
|
|
471
|
+
bind status [--subject <subjectAid>] 核验协议 Owner、任务和同步状态
|
|
468
472
|
|
|
469
473
|
Options:
|
|
470
474
|
--format json 输出 JSON 格式
|
|
@@ -485,6 +489,11 @@ Options:
|
|
|
485
489
|
ec aid agentmd set mybot.agentid.pub description "A helpful agent"`);
|
|
486
490
|
return;
|
|
487
491
|
}
|
|
492
|
+
if (sub === 'bind') {
|
|
493
|
+
const { cmdAidBind } = await import('./aid-bind-command.js');
|
|
494
|
+
await cmdAidBind(args.slice(1));
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
488
497
|
const isAgentmdHelp = sub === 'agentmd' && (!args[1] || isHelpFlag(args[1]));
|
|
489
498
|
if (process.env.EVOLCORE_SESSION_ID && !wantsHelp(args) && !isAgentmdHelp) {
|
|
490
499
|
await runManagedAid(args, formatJson);
|
|
@@ -617,11 +626,11 @@ Options:
|
|
|
617
626
|
process.exit(1);
|
|
618
627
|
}
|
|
619
628
|
try {
|
|
620
|
-
const result = await aidCreate(aid, { aunPath, force });
|
|
629
|
+
const result = await aidCreate(aid, { aunPath, force, type: 'service' });
|
|
621
630
|
let agentmdUploaded = result.alreadyExisted;
|
|
622
631
|
let agentmdError;
|
|
623
632
|
if (!result.alreadyExisted) {
|
|
624
|
-
const content = buildInitialAgentMd({ aid });
|
|
633
|
+
const content = buildInitialAgentMd({ aid, type: 'service' });
|
|
625
634
|
try {
|
|
626
635
|
await agentmdPut(content, { aid, aunPath });
|
|
627
636
|
agentmdUploaded = true;
|
|
@@ -1318,8 +1327,10 @@ Options:
|
|
|
1318
1327
|
*
|
|
1319
1328
|
* `ec msg send --thread T2 --check-every 5m` 发送成功后自动创建。
|
|
1320
1329
|
*
|
|
1321
|
-
* 使用 `interval` 而非 `delay
|
|
1322
|
-
* `ec trigger
|
|
1330
|
+
* 使用 `interval` 而非 `delay`:周期触发,由显式
|
|
1331
|
+
* `ec trigger delete`(结束,不留残留)/ `ec trigger update --every <新时长>`(调整周期)改变状态。
|
|
1332
|
+
* `ec trigger cancel` 只是 disable,定义仍留在磁盘上,复查这种用完即弃的 Trigger
|
|
1333
|
+
* 不该走 cancel,否则会积压大量永不触发的死定义。
|
|
1323
1334
|
* 一次性 Trigger 会静默消失,无法承担「一直跟进直到显式结束」的语义。
|
|
1324
1335
|
*/
|
|
1325
1336
|
export function buildThreadCheckTrigger(input) {
|
|
@@ -1332,21 +1343,24 @@ export function buildThreadCheckTrigger(input) {
|
|
|
1332
1343
|
enabled: true,
|
|
1333
1344
|
createdAt: now,
|
|
1334
1345
|
updatedAt: now,
|
|
1335
|
-
// T10: labels 携带 kind + 业务上下文,渲染层按 kind=thread-check 路由到专属模板
|
|
1336
1346
|
labels: {
|
|
1337
|
-
kind: 'thread-
|
|
1347
|
+
kind: 'cross-thread-review',
|
|
1338
1348
|
originThreadId: input.originThreadId,
|
|
1339
1349
|
targetThreadId: input.targetThreadId,
|
|
1340
1350
|
originPeer: input.originPeer,
|
|
1341
1351
|
checkEvery: input.checkEvery,
|
|
1352
|
+
...(input.originalText ? { originalText: input.originalText } : {}),
|
|
1353
|
+
// labels 只收字符串(validation.ts 校验);scheduler 读取时用 Number() 还原。
|
|
1354
|
+
...(input.firstSentAt ? { firstSentAt: String(input.firstSentAt) } : {}),
|
|
1355
|
+
...(input.originThreadName ? { originThreadName: input.originThreadName } : {}),
|
|
1356
|
+
...(input.targetThreadName ? { targetThreadName: input.targetThreadName } : {}),
|
|
1342
1357
|
},
|
|
1343
1358
|
...(input.origin ? { origin: input.origin } : {}),
|
|
1344
1359
|
// 周期触发:每 everyMs 唤醒一次,直到显式取消/调整
|
|
1345
1360
|
source: { type: 'interval', everyMs: input.everyMs },
|
|
1346
1361
|
execution: {
|
|
1347
|
-
type: '
|
|
1362
|
+
type: 'target_session',
|
|
1348
1363
|
prompt: input.prompt,
|
|
1349
|
-
thread: 'by_trigger',
|
|
1350
1364
|
onError: 'retry',
|
|
1351
1365
|
noopSentinel: '[[NOOP]]',
|
|
1352
1366
|
},
|
|
@@ -1355,7 +1369,8 @@ export function buildThreadCheckTrigger(input) {
|
|
|
1355
1369
|
target: {
|
|
1356
1370
|
channelKey: input.channelKey,
|
|
1357
1371
|
channelId: input.targetChannelId,
|
|
1358
|
-
|
|
1372
|
+
// 话题走 `thread`;主话题走 `main`(threadId 是主话题规范 id,仅作身份记录)。
|
|
1373
|
+
session: isTopicThreadId(input.targetThreadId) ? 'thread' : 'main',
|
|
1359
1374
|
threadId: input.targetThreadId,
|
|
1360
1375
|
chatType: input.feedbackChatType ?? 'private',
|
|
1361
1376
|
},
|
|
@@ -1363,6 +1378,41 @@ export function buildThreadCheckTrigger(input) {
|
|
|
1363
1378
|
reliability: { concurrency: 'forbid', missedPolicy: 'run_once', retry: { maxAttempts: 0, backoffMs: 30_000 } },
|
|
1364
1379
|
};
|
|
1365
1380
|
}
|
|
1381
|
+
/**
|
|
1382
|
+
* 构造跨话题复查的通知正文模板。
|
|
1383
|
+
*
|
|
1384
|
+
* 关键约束(见 docs/thread-chain/cross-thread-review-design.md §3.4):
|
|
1385
|
+
* - 只带**数据上下文**(话题名/ID、时间、次数、周期、Trigger id、原消息),
|
|
1386
|
+
* **不带任何操作命令**——命令与决策判据由 thread-ops skill 提供;
|
|
1387
|
+
* - T1 的 threadId 必须列出:回 T1 汇报或查空洞期都要用它做 `--thread` / `history` 参数;
|
|
1388
|
+
* - T2 的 threadId 不列:收到通知的会话本身就在 T2;
|
|
1389
|
+
* - `【跨话题复查】` 是信封头关键字,供 skill 的关键词触发使用;
|
|
1390
|
+
* - 原消息放最后:正文可能较长,前置字段保持可扫读。
|
|
1391
|
+
*
|
|
1392
|
+
* 模板用 `{{...}}` 占位,触发时由 `renderTemplate` 渲染(scheduler.ts 每次 fire
|
|
1393
|
+
* 都会重新渲染,因此"第几次/已过多久"必须在渲染期计算,不能写死)。
|
|
1394
|
+
*/
|
|
1395
|
+
export function buildCrossThreadReviewPrompt(input) {
|
|
1396
|
+
const truncated = input.originalText && input.originalText.length > 500
|
|
1397
|
+
? `${input.originalText.slice(0, 500)}…`
|
|
1398
|
+
: input.originalText;
|
|
1399
|
+
return [
|
|
1400
|
+
'【跨话题复查】第 {{run.number}} 次',
|
|
1401
|
+
'',
|
|
1402
|
+
`来源话题:${input.originThreadName}(对端 ${input.originPeerName})`,
|
|
1403
|
+
`来源话题 ID:${input.originThreadId}`,
|
|
1404
|
+
`当前话题:${input.targetThreadName}(对端 ${input.targetPeerName})`,
|
|
1405
|
+
`发送于:{{trigger.labels.firstSentAtTime}}`,
|
|
1406
|
+
`已过:{{run.elapsed}}`,
|
|
1407
|
+
`复查周期:${input.checkEvery}(每 ${input.checkEvery} 提醒一次,直到处理)`,
|
|
1408
|
+
'',
|
|
1409
|
+
`Trigger:${input.triggerId}`,
|
|
1410
|
+
...(input.extra?.trim() ? ['', input.extra.trim()] : []),
|
|
1411
|
+
'',
|
|
1412
|
+
'原消息:',
|
|
1413
|
+
truncated || '(非文本消息)',
|
|
1414
|
+
].join('\n');
|
|
1415
|
+
}
|
|
1366
1416
|
/**
|
|
1367
1417
|
* 解析并校验 `--check-every`。返回 `{ durationMs }` 或错误。
|
|
1368
1418
|
* 私聊与群聊两条跨话题发送路径共用,保证错误码一致。
|
|
@@ -1388,19 +1438,54 @@ function parseCheckEveryArgs(checkEveryRaw, hasCheckEvery) {
|
|
|
1388
1438
|
* 设计见 `docs/thread-chain/thread-context-mechanism.md` §十、`thread-cli-spec-v2.md` §3.4。
|
|
1389
1439
|
* 任意一步失败都不影响消息已发出的事实:返回 `triggerId` 为 undefined 时调用方照常输出发送结果。
|
|
1390
1440
|
*/
|
|
1441
|
+
/** Trigger 名称里的话题片段:主话题 id 缩成 `main`(名称不承担身份)。 */
|
|
1442
|
+
function triggerTopicLabel(topicId) {
|
|
1443
|
+
if (!topicId)
|
|
1444
|
+
return 'unknown';
|
|
1445
|
+
return isTopicThreadId(topicId) ? topicId : 'main';
|
|
1446
|
+
}
|
|
1391
1447
|
async function applyCrossThreadSendEffects(input) {
|
|
1392
1448
|
const runtime = await readBestTaskRuntimeContext();
|
|
1393
|
-
const originThreadId = runtime?.threadId || 'main';
|
|
1394
1449
|
const originPeer = runtime?.channelId || input.targetChannelId;
|
|
1450
|
+
// 来源话题 id:话题用原 id;主话题用"本端+对端"拼出的规范 id。
|
|
1451
|
+
// 这里给的是**展示与持久化用**的身份,不会写进线上报文。
|
|
1452
|
+
const originThreadId = isTopicThreadId(runtime?.threadId)
|
|
1453
|
+
? runtime.threadId
|
|
1454
|
+
: (safeFormatMainTopicId(input.selfAid, originPeer) ?? '');
|
|
1395
1455
|
const originSessionId = runtime?.sessionId;
|
|
1396
1456
|
if (!originSessionId)
|
|
1397
1457
|
return { inboxWritten: false };
|
|
1458
|
+
// 话题名:从索引取,取不到时降级到 threadId 或 'main'。
|
|
1459
|
+
// 从 index.js 聚合入口引入:单文件实现路径会绕过测试里对 '../core/thread/index.js'
|
|
1460
|
+
// 的 vi.mock,导致 mock 未覆盖时整个模块被真实加载而报错。
|
|
1461
|
+
// 取值全部包在 try/catch 里:话题名只是通知里的展示信息,索引读失败不能影响
|
|
1462
|
+
// "消息已经发出去了"这个事实,也不能阻止复查创建。
|
|
1463
|
+
let originThreadName = originThreadId || originPeer;
|
|
1464
|
+
let originPeerName = originPeer;
|
|
1465
|
+
let targetThreadName = input.targetThreadId;
|
|
1466
|
+
let targetPeerName = input.targetChannelId;
|
|
1467
|
+
try {
|
|
1468
|
+
const { ThreadService } = await import('../core/thread/index.js');
|
|
1469
|
+
const originEntry = isTopicThreadId(originThreadId)
|
|
1470
|
+
? ThreadService.findThreadById(input.selfAid, originThreadId)
|
|
1471
|
+
: ThreadService.findMainSessionEntry?.(input.selfAid, originPeer);
|
|
1472
|
+
const targetEntry = isTopicThreadId(input.targetThreadId)
|
|
1473
|
+
? ThreadService.findThreadById(input.selfAid, input.targetThreadId)
|
|
1474
|
+
: ThreadService.findMainSessionEntry?.(input.selfAid, input.targetChannelId);
|
|
1475
|
+
originThreadName = originEntry?.name || originThreadName;
|
|
1476
|
+
originPeerName = originEntry?.peerName || originPeerName;
|
|
1477
|
+
targetThreadName = targetEntry?.name || targetThreadName;
|
|
1478
|
+
targetPeerName = targetEntry?.peerName || targetPeerName;
|
|
1479
|
+
}
|
|
1480
|
+
catch {
|
|
1481
|
+
// 索引不可用时保留 threadId / AID 作为降级展示值
|
|
1482
|
+
}
|
|
1398
1483
|
// Trigger 的 id 与 name 是两个不同的东西(见 src/trigger/validation.ts:727):
|
|
1399
|
-
// id = `trig_<ts>_<8位hex>` —— 唯一的持久化标识,`ec trigger show/update/
|
|
1484
|
+
// id = `trig_<ts>_<8位hex>` —— 唯一的持久化标识,`ec trigger show/update/delete` 只认它
|
|
1400
1485
|
// name = `check-<目标threadId>-from-<来源threadId>-<唯一后缀>` —— 人读标签,不参与定位
|
|
1401
1486
|
//
|
|
1402
1487
|
// 这里**在 IPC 之前**就把 id 定下来,有三个理由:
|
|
1403
|
-
// 1. prompt 里要写 `ec trigger
|
|
1488
|
+
// 1. prompt 里要写 `ec trigger delete <id>`,而 prompt 是 definition 的一部分,
|
|
1404
1489
|
// create 之前就得拼完,拿不到 daemon 的返回值;
|
|
1405
1490
|
// 2. definition 带上 id 后 daemon 直接采用(normalizeV3Phase2 认 `raw.id`),
|
|
1406
1491
|
// 落盘 id 与 prompt 中的 id 天然一致,不存在"返回值和 prompt 两套值"的漂移;
|
|
@@ -1412,24 +1497,19 @@ async function applyCrossThreadSendEffects(input) {
|
|
|
1412
1497
|
// (目标, 来源) 对第二次发送就撞 `trigger name already exists`(assertUnique 按 name 去重),
|
|
1413
1498
|
// 结果消息照发、inbox 照写、Trigger 静默不建 —— 完全无感知。
|
|
1414
1499
|
// 每次 `--check-every` 都该有自己的 new Trigger(要复用应走 update),所以追加唯一后缀。
|
|
1415
|
-
const triggerName = `check-${input.targetThreadId}-from-${originThreadId}-${triggerSeed.split('_')[1]}`;
|
|
1416
|
-
const
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
` ec trigger cancel ${triggerId}`,
|
|
1429
|
-
` ec trigger update ${triggerId} --every <时长>`,
|
|
1430
|
-
`Trigger id:${triggerId}`,
|
|
1431
|
-
`Trigger 名称:${triggerName}`,
|
|
1432
|
-
].join('\n');
|
|
1500
|
+
const triggerName = `check-${triggerTopicLabel(input.targetThreadId)}-from-${triggerTopicLabel(originThreadId)}-${triggerSeed.split('_')[1]}`;
|
|
1501
|
+
const prompt = buildCrossThreadReviewPrompt({
|
|
1502
|
+
triggerId,
|
|
1503
|
+
originThreadId,
|
|
1504
|
+
originThreadName,
|
|
1505
|
+
originPeerName,
|
|
1506
|
+
targetThreadName,
|
|
1507
|
+
targetPeerName,
|
|
1508
|
+
checkEvery: input.checkEveryLabel,
|
|
1509
|
+
firstSentAt: input.timestamp,
|
|
1510
|
+
originalText: input.contentSummary,
|
|
1511
|
+
extra: input.triggerPromptExtra,
|
|
1512
|
+
});
|
|
1433
1513
|
let createdTriggerId;
|
|
1434
1514
|
try {
|
|
1435
1515
|
const { formatChannelKey } = await import('../core/channel-loader.js');
|
|
@@ -1446,6 +1526,10 @@ async function applyCrossThreadSendEffects(input) {
|
|
|
1446
1526
|
originThreadId,
|
|
1447
1527
|
originPeer,
|
|
1448
1528
|
feedbackChatType: input.feedbackChatType,
|
|
1529
|
+
originalText: input.contentSummary,
|
|
1530
|
+
firstSentAt: input.timestamp,
|
|
1531
|
+
originThreadName,
|
|
1532
|
+
targetThreadName,
|
|
1449
1533
|
});
|
|
1450
1534
|
const triggerRes = await ipcQuery(resolvePaths().socket, {
|
|
1451
1535
|
type: 'trigger.create',
|
|
@@ -1498,6 +1582,7 @@ Commands:
|
|
|
1498
1582
|
send <from> <to> --file <path> [--as <type>] 发送文件(image|video|voice|file)
|
|
1499
1583
|
send <from> <to> --link <url> [--title T] 发送链接卡片
|
|
1500
1584
|
send <from> <to> --payload <json> 发送自定义 payload
|
|
1585
|
+
request get <self-aid> <request-id> 查询已登记请求状态
|
|
1501
1586
|
pull <from> [--after-seq N] [--limit N] --app <name> 拉取收件箱(必须使用独立 slot)
|
|
1502
1587
|
ack <from> <seq> [--app <name>] 确认已读
|
|
1503
1588
|
recall <from> <message-id> [<message-id>...] 撤回消息
|
|
@@ -1514,6 +1599,9 @@ Options:
|
|
|
1514
1599
|
--app <name> 指定应用 slot(独立消费通道,不影响 daemon)
|
|
1515
1600
|
--aun-path <path> 指定 AUN keystore 根
|
|
1516
1601
|
--format json 输出 JSON 格式
|
|
1602
|
+
--payload '<json>' 发送任意 JSON payload,不添加聊天字段
|
|
1603
|
+
--return none|required 是否等待关联响应(required 建立请求记录)
|
|
1604
|
+
--timeout <duration> required 的等待期限和 AUN volatile TTL,如 30s、60s、2m(默认 60s,最大 3600s)
|
|
1517
1605
|
--encrypt 强制端到端加密(未指定时使用 daemon 默认值)
|
|
1518
1606
|
--no-encrypt 强制明文发送(优先于 --encrypt)
|
|
1519
1607
|
--thread <id> 指定目标话题 ID(话题必须存在于本端索引;from/to 可省略)
|
|
@@ -1525,10 +1613,11 @@ Options:
|
|
|
1525
1613
|
--channel-name <name> 历史渠道实例名(非 AUN 渠道必须匹配 Agent 配置)
|
|
1526
1614
|
--channel-key <key> 非 AUN 已落盘渠道的完整实例键(与 --peer-id 成对)
|
|
1527
1615
|
--peer-id <id> 非 AUN 渠道的原生对端 ID(与 --channel-key 成对)
|
|
1528
|
-
--check-every <duration>
|
|
1529
|
-
每 duration
|
|
1616
|
+
--check-every <duration> 跨话题发送后自动创建**跨话题复查**(如 5m/30m/2h),跨话题时**必填**
|
|
1617
|
+
每 duration 向**目标话题**投递一条【跨话题复查】通知(不新建会话),
|
|
1618
|
+
由目标话题侧的 agent 在自己的上下文里决定结束/调整周期/继续
|
|
1530
1619
|
Trigger 名:check-<目标threadId>-from-<来源threadId>-<唯一后缀>
|
|
1531
|
-
|
|
1620
|
+
结束/调整用返回的 trigger_id(trig_…),show/update/delete 只认 id
|
|
1532
1621
|
--content-type <mime> 显式覆盖 MIME(仅 --file 模式)
|
|
1533
1622
|
--as <type> 附件类型:image|video|voice|file(仅 --file 模式)
|
|
1534
1623
|
--text <说明> 附件说明文字(仅 --file 模式)
|
|
@@ -1545,6 +1634,8 @@ Options:
|
|
|
1545
1634
|
ec msg send alice.agentid.pub bob.agentid.pub --file ./pic.png
|
|
1546
1635
|
ec msg send alice.agentid.pub bob.agentid.pub --file ./demo.mp4 --as video
|
|
1547
1636
|
ec msg send alice.agentid.pub bob.agentid.pub --link https://example.com --title "AUN"
|
|
1637
|
+
ec msg send alice.agentid.pub bob.agentid.pub --payload '{"type":"query"}' --return required --timeout 60s --format json
|
|
1638
|
+
ec msg request get alice.agentid.pub request-123 --format json
|
|
1548
1639
|
ec msg pull alice.agentid.pub --app my-bot
|
|
1549
1640
|
ec msg ack alice.agentid.pub 42 --app my-bot
|
|
1550
1641
|
ec msg recall alice.agentid.pub msg-uuid-1 msg-uuid-2
|
|
@@ -1584,6 +1675,41 @@ Options:
|
|
|
1584
1675
|
}
|
|
1585
1676
|
// P1b.6.2: 托管会话 send 时,from/to 可省略——从 runtime 和话题索引推断
|
|
1586
1677
|
// history --thread 时同理:from/to 均可省略
|
|
1678
|
+
//
|
|
1679
|
+
// 本端来源:托管会话里恒取**当前会话的本端**(`runtime.selfAid`)——这正是
|
|
1680
|
+
// "主话题 id 隐含本端"成立的前提。显式 `--self` 只用于宿主终端(下方分支),
|
|
1681
|
+
// 托管会话里写了就必须与当前本端一致,不允许拿它去冒充别的本端。
|
|
1682
|
+
if (sub === 'request' && args[1] === 'get') {
|
|
1683
|
+
const aid = args[2];
|
|
1684
|
+
const requestId = args[3];
|
|
1685
|
+
if (!aid || !requestId) {
|
|
1686
|
+
console.error('usage: ec msg request get <self-aid> <request-id> [--format json]');
|
|
1687
|
+
process.exit(1);
|
|
1688
|
+
}
|
|
1689
|
+
const runtime = await readBestTaskRuntimeContext();
|
|
1690
|
+
if (runtime?.selfAid && runtime.selfAid.replace(/^@/, '') !== aid) {
|
|
1691
|
+
console.error(`request status is limited to the delegated self AID: ${runtime.selfAid}`);
|
|
1692
|
+
process.exit(1);
|
|
1693
|
+
}
|
|
1694
|
+
const result = await ipcQuery(resolvePaths().socket, {
|
|
1695
|
+
type: 'aun-msg-request-get', aid, requestId,
|
|
1696
|
+
sessionId: process.env.EVOLCORE_SESSION_ID,
|
|
1697
|
+
delegationToken: readDelegationCredential(),
|
|
1698
|
+
}, 10_000);
|
|
1699
|
+
if (!result) {
|
|
1700
|
+
console.error('unable to connect to the EvolCore daemon');
|
|
1701
|
+
process.exit(1);
|
|
1702
|
+
}
|
|
1703
|
+
if (formatJson)
|
|
1704
|
+
console.log(JSON.stringify(result));
|
|
1705
|
+
else if (!result.ok)
|
|
1706
|
+
console.error(`request query failed: ${result.error}`);
|
|
1707
|
+
else
|
|
1708
|
+
console.log(`request_id=${requestId} state=${result.request?.state ?? 'unknown'} message_id=${result.request?.outboundMessageId ?? '-'}`);
|
|
1709
|
+
if (!result.ok)
|
|
1710
|
+
process.exit(1);
|
|
1711
|
+
return;
|
|
1712
|
+
}
|
|
1587
1713
|
if (process.env.EVOLCORE_SESSION_ID && (sub === 'send' || (sub === 'history' && getArgValue(args, '--thread')))) {
|
|
1588
1714
|
const { isValidAid: checkAid } = await import('../aun/aid/index.js');
|
|
1589
1715
|
const hasFrom = typeof args[1] === 'string' && checkAid(args[1]);
|
|
@@ -1592,13 +1718,36 @@ Options:
|
|
|
1592
1718
|
const runtime = await readBestTaskRuntimeContext();
|
|
1593
1719
|
if (runtime?.selfAid) {
|
|
1594
1720
|
const inferredFrom = runtime.selfAid.replace(/^@/, '');
|
|
1721
|
+
const explicitSelf = getArgValue(args, '--self')?.replace(/^@/, '');
|
|
1722
|
+
if (explicitSelf && explicitSelf !== inferredFrom) {
|
|
1723
|
+
console.error(`❌ 当前会话的本端是 ${inferredFrom},--self 不能指定 ${explicitSelf};跨本端发送只能由该本端自己的会话发起`);
|
|
1724
|
+
process.exit(1);
|
|
1725
|
+
}
|
|
1595
1726
|
let inferredTo;
|
|
1596
1727
|
const threadArg = getArgValue(args, '--thread');
|
|
1597
1728
|
if (threadArg) {
|
|
1598
|
-
|
|
1599
|
-
|
|
1729
|
+
// 目标对端只能从话题引用里推导:主话题 id 自带另一端,话题查本端索引。
|
|
1730
|
+
// 解析失败必须直接失败——回落到"当前对端"会把一条本应发往别处的消息
|
|
1731
|
+
// 静默投进当前会话。
|
|
1732
|
+
const { resolveThreadRef } = await import('../core/thread/topic-id.js');
|
|
1733
|
+
const resolved = resolveThreadRef(threadArg, inferredFrom);
|
|
1734
|
+
if (resolved.kind === 'main') {
|
|
1735
|
+
inferredTo = resolved.peer;
|
|
1736
|
+
}
|
|
1737
|
+
else if (resolved.kind === 'thread') {
|
|
1738
|
+
const { ThreadService } = await import('../core/thread/index.js');
|
|
1739
|
+
inferredTo = resolved.peer ?? ThreadService.findThreadById(inferredFrom, resolved.threadId)?.peer;
|
|
1740
|
+
}
|
|
1741
|
+
else if (resolved.kind === 'invalid') {
|
|
1742
|
+
console.error(`❌ ${resolved.error}`);
|
|
1743
|
+
process.exit(1);
|
|
1744
|
+
}
|
|
1745
|
+
if (!inferredTo) {
|
|
1746
|
+
console.error(`❌ 无法从话题引用确定目标对端: ${threadArg}(话题必须存在于本端索引)`);
|
|
1747
|
+
process.exit(1);
|
|
1748
|
+
}
|
|
1600
1749
|
}
|
|
1601
|
-
|
|
1750
|
+
else {
|
|
1602
1751
|
inferredTo = runtime.channelId?.replace(/^@/, '');
|
|
1603
1752
|
}
|
|
1604
1753
|
if (!hasFrom && !hasTo && inferredTo) {
|
|
@@ -1613,6 +1762,45 @@ Options:
|
|
|
1613
1762
|
}
|
|
1614
1763
|
}
|
|
1615
1764
|
}
|
|
1765
|
+
// 宿主终端的简化式:`ec msg send '<正文>' --self <selfAid> --thread <ref>`
|
|
1766
|
+
// 没有托管会话上下文,本端只能由 --self 给出;对端由话题引用推导。
|
|
1767
|
+
// 这和托管会话里的简化式是同一语义,只是本端来源不同。
|
|
1768
|
+
if (sub === 'send' && !process.env.EVOLCORE_SESSION_ID) {
|
|
1769
|
+
const { isValidAid: checkAid } = await import('../aun/aid/index.js');
|
|
1770
|
+
const selfArg = getArgValue(args, '--self')?.replace(/^@/, '');
|
|
1771
|
+
const threadArg = getArgValue(args, '--thread');
|
|
1772
|
+
const hasFrom = typeof args[1] === 'string' && checkAid(args[1]);
|
|
1773
|
+
const hasTo = typeof args[2] === 'string' && checkAid(args[2]);
|
|
1774
|
+
if (threadArg && (!hasFrom || !hasTo)) {
|
|
1775
|
+
if (!selfArg || !checkAid(selfArg)) {
|
|
1776
|
+
console.error('❌ 省略 <from> <to> 时必须用 --self <本端AID> 指定本端(对端由 --thread 推导)');
|
|
1777
|
+
process.exit(1);
|
|
1778
|
+
}
|
|
1779
|
+
const { resolveThreadRef } = await import('../core/thread/topic-id.js');
|
|
1780
|
+
const resolved = resolveThreadRef(threadArg, selfArg);
|
|
1781
|
+
if (resolved.kind === 'invalid' || resolved.kind === 'current') {
|
|
1782
|
+
console.error(`❌ ${resolved.kind === 'invalid' ? resolved.error : '无效的话题引用'}`);
|
|
1783
|
+
process.exit(1);
|
|
1784
|
+
}
|
|
1785
|
+
let peer;
|
|
1786
|
+
if (resolved.kind === 'main')
|
|
1787
|
+
peer = resolved.peer;
|
|
1788
|
+
else {
|
|
1789
|
+
const { ThreadService } = await import('../core/thread/index.js');
|
|
1790
|
+
peer = resolved.peer ?? ThreadService.findThreadById(selfArg, resolved.threadId)?.peer;
|
|
1791
|
+
}
|
|
1792
|
+
if (!peer) {
|
|
1793
|
+
console.error(`❌ 无法从话题引用确定目标对端: ${threadArg}(话题必须存在于本端索引)`);
|
|
1794
|
+
process.exit(1);
|
|
1795
|
+
}
|
|
1796
|
+
if (!hasFrom && !hasTo)
|
|
1797
|
+
args.splice(1, 0, selfArg, peer);
|
|
1798
|
+
else if (!hasFrom)
|
|
1799
|
+
args.splice(1, 0, selfArg);
|
|
1800
|
+
else
|
|
1801
|
+
args.splice(2, 0, peer);
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1616
1804
|
const from = args[1];
|
|
1617
1805
|
if (!from) {
|
|
1618
1806
|
console.error('❌ 缺少 <from-aid> 参数');
|
|
@@ -1621,6 +1809,9 @@ Options:
|
|
|
1621
1809
|
const { isValidAid } = await import('../aun/aid/index.js');
|
|
1622
1810
|
if (!isValidAid(from)) {
|
|
1623
1811
|
console.error(`❌ 无效 AID 格式: ${from}`);
|
|
1812
|
+
if (sub === 'send') {
|
|
1813
|
+
console.error('提示:宿主终端可用 `ec msg send \'<正文>\' --self <本端AID> --thread <话题id> --check-every 5m`');
|
|
1814
|
+
}
|
|
1624
1815
|
process.exit(1);
|
|
1625
1816
|
}
|
|
1626
1817
|
if (process.env.EVOLCORE_SESSION_ID && sub !== 'send') {
|
|
@@ -1649,7 +1840,7 @@ Options:
|
|
|
1649
1840
|
if (sub === 'history') {
|
|
1650
1841
|
const target = args[2] && !args[2].startsWith('--') ? args[2] : undefined;
|
|
1651
1842
|
const historyValueFlags = new Set([
|
|
1652
|
-
'--session', '--limit', '--before', '--after', '--direction', '--channel', '--channel-name', '--format', '--channel-key', '--peer-id',
|
|
1843
|
+
'--session', '--limit', '--before', '--after', '--direction', '--channel', '--channel-name', '--format', '--channel-key', '--peer-id', '--thread', '--self',
|
|
1653
1844
|
]);
|
|
1654
1845
|
const seenHistoryFlags = new Set();
|
|
1655
1846
|
for (let index = target ? 3 : 2; index < args.length; index++) {
|
|
@@ -1679,14 +1870,14 @@ Options:
|
|
|
1679
1870
|
const limit = limitRaw === undefined ? undefined : Number(limitRaw);
|
|
1680
1871
|
const beforeRaw = getArgValue(args, '--before');
|
|
1681
1872
|
const afterRaw = getArgValue(args, '--after');
|
|
1682
|
-
const before = beforeRaw === undefined ? undefined :
|
|
1683
|
-
const after = afterRaw === undefined ? undefined :
|
|
1873
|
+
const before = beforeRaw === undefined ? undefined : parseTimeArg(beforeRaw);
|
|
1874
|
+
const after = afterRaw === undefined ? undefined : parseTimeArg(afterRaw);
|
|
1684
1875
|
if (beforeRaw !== undefined && before === undefined) {
|
|
1685
|
-
console.error(`❌ --before
|
|
1876
|
+
console.error(`❌ --before 不是有效时间: ${beforeRaw}(支持 ${TIME_ARG_FORMATS})`);
|
|
1686
1877
|
process.exit(1);
|
|
1687
1878
|
}
|
|
1688
1879
|
if (afterRaw !== undefined && after === undefined) {
|
|
1689
|
-
console.error(`❌ --after
|
|
1880
|
+
console.error(`❌ --after 不是有效时间: ${afterRaw}(支持 ${TIME_ARG_FORMATS})`);
|
|
1690
1881
|
process.exit(1);
|
|
1691
1882
|
}
|
|
1692
1883
|
const channelKey = getArgValue(args, '--channel-key');
|
|
@@ -1725,6 +1916,18 @@ Options:
|
|
|
1725
1916
|
process.exit(1);
|
|
1726
1917
|
}
|
|
1727
1918
|
}
|
|
1919
|
+
// `--thread` 接受 `ec thread list` 打印的 `<peer>/<id>`;前缀必须就是本次查询的对端
|
|
1920
|
+
let historyThreadId;
|
|
1921
|
+
const historyThreadRaw = getArgValue(args, '--thread');
|
|
1922
|
+
if (historyThreadRaw !== undefined) {
|
|
1923
|
+
const { normalizeThreadOptionValue } = await import('../core/thread/topic-id.js');
|
|
1924
|
+
const normalized = normalizeThreadOptionValue(historyThreadRaw, peer => peer === target?.replace(/^@/, ''));
|
|
1925
|
+
if (!normalized.ok) {
|
|
1926
|
+
console.error(`❌ ${normalized.error}`);
|
|
1927
|
+
process.exit(1);
|
|
1928
|
+
}
|
|
1929
|
+
historyThreadId = normalized.threadId;
|
|
1930
|
+
}
|
|
1728
1931
|
const result = msgHistory({
|
|
1729
1932
|
from,
|
|
1730
1933
|
...(usesSelector
|
|
@@ -1735,7 +1938,7 @@ Options:
|
|
|
1735
1938
|
before,
|
|
1736
1939
|
after,
|
|
1737
1940
|
direction: (getArgValue(args, '--direction') ?? 'all'),
|
|
1738
|
-
threadId:
|
|
1941
|
+
threadId: historyThreadId,
|
|
1739
1942
|
});
|
|
1740
1943
|
if (!result.ok) {
|
|
1741
1944
|
if (format === 'json')
|
|
@@ -1824,7 +2027,7 @@ Options:
|
|
|
1824
2027
|
else if (payloadVal) {
|
|
1825
2028
|
let parsed;
|
|
1826
2029
|
try {
|
|
1827
|
-
parsed =
|
|
2030
|
+
parsed = parseJsonObjectArgument(payloadVal);
|
|
1828
2031
|
}
|
|
1829
2032
|
catch (e) {
|
|
1830
2033
|
console.error(`❌ --payload 解析失败: ${e.message}`);
|
|
@@ -1845,27 +2048,50 @@ Options:
|
|
|
1845
2048
|
? false
|
|
1846
2049
|
: args.includes('--encrypt') ? true : undefined;
|
|
1847
2050
|
const thread = getArgValue(args, '--thread');
|
|
1848
|
-
// P1b.6.5: 话题存在性校验 —
|
|
2051
|
+
// P1b.6.5: 话题存在性校验 — 引用必须能解析成本端已存在的话题
|
|
1849
2052
|
let threadEntry;
|
|
2053
|
+
// 复查 Trigger 里记录的"目标话题"必须是规范 id:话题原样,主话题用
|
|
2054
|
+
// `main:<a>:<b>`(`--thread` 可能写的是裸对端 AID 或 `<peer>/<id>` 历史写法)。
|
|
2055
|
+
let threadTopicId = thread ?? '';
|
|
1850
2056
|
if (thread) {
|
|
1851
2057
|
const { ThreadService, normalizeThreadPeer } = await import('../core/thread/index.js');
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
else
|
|
1858
|
-
console.error(`❌
|
|
1859
|
-
}
|
|
2058
|
+
const { resolveThreadRef, isMainSessionThreadId } = await import('../core/thread/topic-id.js');
|
|
2059
|
+
const resolved = resolveThreadRef(thread, from);
|
|
2060
|
+
const fail = (message) => {
|
|
2061
|
+
if (formatJson)
|
|
2062
|
+
console.log(JSON.stringify({ ok: false, code: 'TOPIC_NOT_FOUND', error: message }));
|
|
2063
|
+
else
|
|
2064
|
+
console.error(`❌ ${message}`);
|
|
1860
2065
|
process.exit(1);
|
|
2066
|
+
};
|
|
2067
|
+
if (resolved.kind === 'invalid' || resolved.kind === 'current')
|
|
2068
|
+
fail(resolved.kind === 'invalid' ? resolved.error : '无效的话题引用');
|
|
2069
|
+
else if (resolved.kind === 'main') {
|
|
2070
|
+
if (normalizeThreadPeer(resolved.peer) !== normalizeThreadPeer(to))
|
|
2071
|
+
fail(`主话题 ${thread} 的对端不是 ${to}`);
|
|
2072
|
+
const main = await ThreadService.findMainSessionEntry(from, resolved.peer);
|
|
2073
|
+
if (!main)
|
|
2074
|
+
fail(`与 ${resolved.peer} 无主话题会话`);
|
|
2075
|
+
threadEntry = main;
|
|
2076
|
+
threadTopicId = resolved.topicId;
|
|
2077
|
+
}
|
|
2078
|
+
else {
|
|
2079
|
+
threadEntry = ThreadService.findThreadById(from, resolved.threadId);
|
|
2080
|
+
const peer = resolved.peer ?? threadEntry?.peer;
|
|
2081
|
+
if (!threadEntry || !peer || normalizeThreadPeer(threadEntry.peer) !== normalizeThreadPeer(to)) {
|
|
2082
|
+
fail(`话题不存在: ${thread}`);
|
|
2083
|
+
}
|
|
2084
|
+
threadTopicId = resolved.threadId;
|
|
1861
2085
|
}
|
|
1862
2086
|
}
|
|
1863
|
-
// T8:
|
|
2087
|
+
// T8: 跨话题发送必须显式指定复查周期(发送前校验,避免消息已发出才报错)。
|
|
2088
|
+
// 仅托管会话(有 runtime.sessionId)才会创建复查 Trigger,宿主终端直发不受此限制。
|
|
1864
2089
|
const checkEveryRaw = getArgValue(args, '--check-every');
|
|
1865
2090
|
const hasCheckEvery = args.includes('--check-every') || checkEveryRaw !== undefined;
|
|
1866
2091
|
const triggerPromptExtra = getArgValue(args, '--trigger-prompt');
|
|
1867
2092
|
let checkEveryDurationMs;
|
|
1868
|
-
|
|
2093
|
+
const runtime = await readBestTaskRuntimeContext();
|
|
2094
|
+
if (thread && runtime?.sessionId) {
|
|
1869
2095
|
const parsedCheck = parseCheckEveryArgs(checkEveryRaw, hasCheckEvery);
|
|
1870
2096
|
if ('error' in parsedCheck) {
|
|
1871
2097
|
if (formatJson)
|
|
@@ -1876,16 +2102,35 @@ Options:
|
|
|
1876
2102
|
}
|
|
1877
2103
|
checkEveryDurationMs = parsedCheck.durationMs;
|
|
1878
2104
|
}
|
|
1879
|
-
// --return 参数已移除:跨会话 send 不再需要指定返回策略(内部默认 none)。
|
|
1880
2105
|
// SDK 尚未提供字节进度回调,只展示上传开始和成功完成。
|
|
2106
|
+
const returnPolicyRaw = getArgValue(args, '--return');
|
|
2107
|
+
if (returnPolicyRaw !== undefined && returnPolicyRaw !== 'none' && returnPolicyRaw !== 'required') {
|
|
2108
|
+
console.error('invalid --return; expected none|required');
|
|
2109
|
+
process.exit(1);
|
|
2110
|
+
}
|
|
2111
|
+
const timeoutRaw = getArgValue(args, '--timeout');
|
|
2112
|
+
if (timeoutRaw !== undefined && returnPolicyRaw !== 'required') {
|
|
2113
|
+
console.error('--timeout requires --return required');
|
|
2114
|
+
process.exit(1);
|
|
2115
|
+
}
|
|
2116
|
+
let timeoutMs;
|
|
2117
|
+
if (timeoutRaw !== undefined) {
|
|
2118
|
+
const parsedTimeout = parseDuration(timeoutRaw);
|
|
2119
|
+
if (parsedTimeout === null || parsedTimeout <= 0) {
|
|
2120
|
+
console.error(`invalid --timeout: ${timeoutRaw}`);
|
|
2121
|
+
process.exit(1);
|
|
2122
|
+
}
|
|
2123
|
+
timeoutMs = parsedTimeout;
|
|
2124
|
+
}
|
|
1881
2125
|
const onUploadProgress = formatJson ? undefined : (info) => {
|
|
1882
2126
|
if (info.phase === 'upload')
|
|
1883
2127
|
process.stderr.write(' ⏫ uploading...\n');
|
|
1884
2128
|
else if (info.phase === 'done')
|
|
1885
2129
|
process.stderr.write(' ✓ upload complete\n');
|
|
1886
2130
|
};
|
|
1887
|
-
|
|
1888
|
-
|
|
2131
|
+
// `--thread` 的原文只用于解析;发出的必须是规范 id(历史写法 `<peer>/<id>`
|
|
2132
|
+
// 若原样上线,对端会把它当成一个不存在的话题 id)。
|
|
2133
|
+
const result = await msgSend({ from, to, body, encrypt, thread: thread ? threadTopicId : undefined, returnPolicy: returnPolicyRaw, timeoutMs, handoffContext: getArgValue(args, '--handoff-context'), onUploadProgress, ...commonOpts });
|
|
1889
2134
|
if (!result.ok) {
|
|
1890
2135
|
if (formatJson) {
|
|
1891
2136
|
console.log(JSON.stringify(result));
|
|
@@ -1901,7 +2146,7 @@ Options:
|
|
|
1901
2146
|
if (thread && threadEntry && result.message_id && runtime?.sessionId) {
|
|
1902
2147
|
const effects = await applyCrossThreadSendEffects({
|
|
1903
2148
|
selfAid: from,
|
|
1904
|
-
targetThreadId:
|
|
2149
|
+
targetThreadId: threadTopicId,
|
|
1905
2150
|
targetSessionId: threadEntry.sessionId,
|
|
1906
2151
|
targetChannelId: to,
|
|
1907
2152
|
feedbackChatType: 'private',
|
|
@@ -1920,7 +2165,10 @@ Options:
|
|
|
1920
2165
|
// E4: 把复查 Trigger ID 回填到返回值,兑现 spec §3.4 的 `{ message_id, trigger_id }` 契约。
|
|
1921
2166
|
if (createdTriggerId)
|
|
1922
2167
|
result.trigger_id = createdTriggerId;
|
|
1923
|
-
if (formatJson) {
|
|
2168
|
+
if (result.request_id && !formatJson) {
|
|
2169
|
+
console.log(`request_id=${result.request_id} state=${result.request_state ?? '-'} message_id=${result.message_id ?? '-'}`);
|
|
2170
|
+
}
|
|
2171
|
+
else if (formatJson) {
|
|
1924
2172
|
console.log(JSON.stringify(result));
|
|
1925
2173
|
}
|
|
1926
2174
|
else {
|
|
@@ -2178,10 +2426,11 @@ Options:
|
|
|
2178
2426
|
--encrypt 强制端到端加密(未指定时使用 daemon 默认值;仅 send)
|
|
2179
2427
|
--no-encrypt 强制明文发送(优先于 --encrypt;仅 send)
|
|
2180
2428
|
--thread <id> 指定话题 ID(仅 send;话题必须存在于本端索引)
|
|
2181
|
-
--check-every <时长> 跨话题发送(带 --thread
|
|
2429
|
+
--check-every <时长> 跨话题发送(带 --thread)时**必填**,创建跨话题复查(如 5m/30m/2h)
|
|
2430
|
+
到期向**目标话题**投递【跨话题复查】通知,不新建会话
|
|
2182
2431
|
无值时默认 5m;Trigger 名:check-<目标threadId>-from-<来源threadId>-<唯一后缀>
|
|
2183
|
-
|
|
2184
|
-
--trigger-prompt <text>
|
|
2432
|
+
结束/调整用返回的 trigger_id(trig_…),show/update/delete 只认 id
|
|
2433
|
+
--trigger-prompt <text> 追加到复查通知正文里的补充说明(仅 --check-every)
|
|
2185
2434
|
--mention <...> 发送时 @ 某个成员。单引号包住、逗号分隔多个 aid
|
|
2186
2435
|
(如 --mention 'alice.agentid.pub,bob.agentid.pub'),可多次传入
|
|
2187
2436
|
--mention-all 发送时 @ 所有人
|
|
@@ -2514,7 +2763,7 @@ g- 是名称的一部分,会完整保留;默认命名格式为 g-10000 至 g
|
|
|
2514
2763
|
else if (payloadVal) {
|
|
2515
2764
|
let parsed;
|
|
2516
2765
|
try {
|
|
2517
|
-
parsed =
|
|
2766
|
+
parsed = parseJsonObjectArgument(payloadVal);
|
|
2518
2767
|
}
|
|
2519
2768
|
catch (e) {
|
|
2520
2769
|
console.error(`❌ --payload 解析失败: ${e.message}`);
|
|
@@ -2550,12 +2799,35 @@ g- 是名称的一部分,会完整保留;默认命名格式为 g-10000 至 g
|
|
|
2550
2799
|
const groupTriggerPromptExtra = getSendArgValue('--trigger-prompt');
|
|
2551
2800
|
let groupCheckEveryMs;
|
|
2552
2801
|
let groupThreadEntry;
|
|
2802
|
+
// 与私聊路径同口径:复查 Trigger 记录规范 id,不记录调用方写的引用原文。
|
|
2803
|
+
let groupTopicId = thread ?? '';
|
|
2553
2804
|
if (thread) {
|
|
2554
|
-
//
|
|
2805
|
+
// 引用必须解析成本端已存在的话题(与私聊路径一致,不自动创建)。
|
|
2555
2806
|
const { ThreadService } = await import('../core/thread/index.js');
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2807
|
+
const { resolveThreadRef } = await import('../core/thread/topic-id.js');
|
|
2808
|
+
const resolved = resolveThreadRef(thread, from);
|
|
2809
|
+
if (resolved.kind === 'invalid' || resolved.kind === 'current') {
|
|
2810
|
+
failManagedGroup(formatJson, 'TOPIC_NOT_FOUND', resolved.kind === 'invalid' ? resolved.error : '无效的话题引用');
|
|
2811
|
+
}
|
|
2812
|
+
else if (resolved.kind === 'main') {
|
|
2813
|
+
if (normalizeGroupAid(resolved.peer, from.slice(from.indexOf('.') + 1)) !== groupId) {
|
|
2814
|
+
failManagedGroup(formatJson, 'TOPIC_NOT_FOUND', `主话题 ${thread} 的对端不是 ${groupId}`);
|
|
2815
|
+
}
|
|
2816
|
+
groupThreadEntry = ThreadService.findMainSessionEntry(from, resolved.peer);
|
|
2817
|
+
if (!groupThreadEntry)
|
|
2818
|
+
failManagedGroup(formatJson, 'TOPIC_NOT_FOUND', `与 ${resolved.peer} 无主话题会话`);
|
|
2819
|
+
groupTopicId = resolved.topicId;
|
|
2820
|
+
}
|
|
2821
|
+
else {
|
|
2822
|
+
const entry = ThreadService.findThreadById(from, resolved.threadId);
|
|
2823
|
+
const peer = resolved.peer ?? entry?.peer;
|
|
2824
|
+
if (!entry || !peer
|
|
2825
|
+
|| normalizeGroupAid(entry.peer, from.slice(from.indexOf('.') + 1)) !== groupId
|
|
2826
|
+
|| normalizeGroupAid(peer, from.slice(from.indexOf('.') + 1)) !== groupId) {
|
|
2827
|
+
failManagedGroup(formatJson, 'TOPIC_NOT_FOUND', `话题不存在: ${thread}`);
|
|
2828
|
+
}
|
|
2829
|
+
groupThreadEntry = entry;
|
|
2830
|
+
groupTopicId = resolved.threadId;
|
|
2559
2831
|
}
|
|
2560
2832
|
const parsedCheck = parseCheckEveryArgs(groupCheckEveryRaw, groupHasCheckEvery);
|
|
2561
2833
|
if ('error' in parsedCheck) {
|
|
@@ -2569,7 +2841,7 @@ g- 是名称的一部分,会完整保留;默认命名格式为 g-10000 至 g
|
|
|
2569
2841
|
body,
|
|
2570
2842
|
mentions: mentions.length ? mentions : undefined,
|
|
2571
2843
|
encrypt: encryptGroup,
|
|
2572
|
-
thread,
|
|
2844
|
+
thread: thread ? groupTopicId : undefined,
|
|
2573
2845
|
...commonOpts,
|
|
2574
2846
|
});
|
|
2575
2847
|
// T2.6: 群聊跨话题发送成功后,同样写入本端 inbox(与私聊路径一致)
|
|
@@ -2579,7 +2851,7 @@ g- 是名称的一部分,会完整保留;默认命名格式为 g-10000 至 g
|
|
|
2579
2851
|
if (messageId) {
|
|
2580
2852
|
const effects = await applyCrossThreadSendEffects({
|
|
2581
2853
|
selfAid: from,
|
|
2582
|
-
targetThreadId:
|
|
2854
|
+
targetThreadId: groupTopicId,
|
|
2583
2855
|
targetSessionId: groupThreadEntry.sessionId,
|
|
2584
2856
|
targetChannelId: groupId,
|
|
2585
2857
|
feedbackChatType: 'group',
|
|
@@ -3123,13 +3395,27 @@ async function runManagedSend(argv, formatJson) {
|
|
|
3123
3395
|
if (!result)
|
|
3124
3396
|
failManagedGroup(formatJson, 'AUN_DAEMON_UNAVAILABLE', 'Unable to connect to the EvolCore daemon');
|
|
3125
3397
|
if (!result.ok)
|
|
3126
|
-
failManagedGroup(formatJson, result.code ?? 'AUN_SEND_FAILED', result.error ?? 'Send failed'
|
|
3398
|
+
failManagedGroup(formatJson, result.code ?? 'AUN_SEND_FAILED', `${result.error ?? 'Send failed'}${result.request_id ? ` request_id=${result.request_id}` : ''}`, {
|
|
3399
|
+
...(typeof result.detail_code === 'string' ? { detail_code: result.detail_code } : {}),
|
|
3400
|
+
...(typeof result.retryable === 'boolean' ? { retryable: result.retryable } : {}),
|
|
3401
|
+
...(typeof result.sent === 'boolean' ? { sent: result.sent } : {}),
|
|
3402
|
+
});
|
|
3127
3403
|
if (formatJson)
|
|
3128
3404
|
console.log(JSON.stringify(result));
|
|
3129
3405
|
else if (result.handoff_id)
|
|
3130
3406
|
console.log(`✓ handoff ${result.handoff_id} ${result.status === 'delivered' ? '已投递' : '已排队'} ${result.message_id ?? ''}`.trimEnd());
|
|
3407
|
+
else if (result.request_id)
|
|
3408
|
+
console.log(`sent message_id=${result.message_id ?? '-'} request_id=${result.request_id} state=${result.request_state ?? '-'}`);
|
|
3131
3409
|
else
|
|
3132
3410
|
console.log(`✓ 已发送 message_id=${result.message_id ?? '-'} seq=${result.seq ?? result.message?.seq ?? '-'}`);
|
|
3411
|
+
// 复查 Trigger 结果必须可见:消息已发出所以不改退出码(避免调用方重发),
|
|
3412
|
+
// 但没建成要明说,别让调用方以为"N 分钟后会自动复查"。
|
|
3413
|
+
if (!formatJson) {
|
|
3414
|
+
if (result.trigger_id)
|
|
3415
|
+
console.log(`✓ 复查 Trigger ${result.trigger_id}`);
|
|
3416
|
+
else if (result.trigger_error)
|
|
3417
|
+
process.stderr.write(`⚠ 复查 Trigger 未创建: ${result.trigger_error}(消息已发出,需要复查请手动 ec trigger create)\n`);
|
|
3418
|
+
}
|
|
3133
3419
|
}
|
|
3134
3420
|
export function resolveManagedSendArgv(argv) {
|
|
3135
3421
|
if (!['msg', 'group'].includes(argv[0]) || argv[1] !== 'send' || !validSendAid(argv[2]))
|
|
@@ -3143,7 +3429,7 @@ export function resolveManagedSendArgv(argv) {
|
|
|
3143
3429
|
const booleans = new Set();
|
|
3144
3430
|
const text = [];
|
|
3145
3431
|
const mentions = [];
|
|
3146
|
-
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']);
|
|
3432
|
+
const valueFlags = new Set(['--format', '--file', '--payload', '--text-from-file', '--link', '--title', '--description', '--as', '--content-type', '--text', '--transcript', '--thread', '--check-every', '--trigger-prompt', '--return', '--timeout', '--handoff-context', '--mention', '--self']);
|
|
3147
3433
|
const booleanFlags = new Set(['--encrypt', '--no-encrypt', '--mention-all']);
|
|
3148
3434
|
for (let index = 4; index < argv.length; index++) {
|
|
3149
3435
|
const flag = argv[index];
|
|
@@ -3182,9 +3468,28 @@ export function resolveManagedSendArgv(argv) {
|
|
|
3182
3468
|
const returnPolicy = values.get('--return');
|
|
3183
3469
|
if (returnPolicy !== undefined && !['none', 'required'].includes(returnPolicy))
|
|
3184
3470
|
throw new Error('Invalid return policy');
|
|
3471
|
+
if (scope === 'group' && returnPolicy === 'required')
|
|
3472
|
+
throw new Error('--return required is supported for private msg sends only');
|
|
3473
|
+
const timeoutRaw = values.get('--timeout');
|
|
3474
|
+
if (timeoutRaw !== undefined && returnPolicy !== 'required')
|
|
3475
|
+
throw new Error('--timeout requires --return required');
|
|
3476
|
+
let timeoutMs;
|
|
3477
|
+
if (timeoutRaw !== undefined) {
|
|
3478
|
+
const parsedTimeout = parseDuration(timeoutRaw);
|
|
3479
|
+
if (parsedTimeout === null || parsedTimeout <= 0)
|
|
3480
|
+
throw new Error('Invalid --timeout duration');
|
|
3481
|
+
if (returnPolicy === 'required' && parsedTimeout > 3_600_000)
|
|
3482
|
+
throw new Error('required request timeout cannot exceed 3600s for volatile AUN messages');
|
|
3483
|
+
timeoutMs = parsedTimeout;
|
|
3484
|
+
}
|
|
3185
3485
|
const handoffContext = values.get('--handoff-context');
|
|
3186
3486
|
if (handoffContext !== undefined && (scope !== 'msg' || !handoffContext.trim() || handoffContext.length > 65536))
|
|
3187
3487
|
throw new Error('Invalid private handoff context');
|
|
3488
|
+
// `--self` 在托管发送里只作冗余声明:本端必须就是当前会话的本端。
|
|
3489
|
+
const declaredSelf = values.get('--self')?.replace(/^@/, '');
|
|
3490
|
+
if (declaredSelf !== undefined && declaredSelf !== aid) {
|
|
3491
|
+
throw new Error(`--self ${declaredSelf} 与当前会话本端 ${aid} 不一致`);
|
|
3492
|
+
}
|
|
3188
3493
|
const thread = values.get('--thread');
|
|
3189
3494
|
const checkEvery = values.get('--check-every');
|
|
3190
3495
|
const triggerPromptExtra = values.get('--trigger-prompt');
|
|
@@ -3217,9 +3522,7 @@ export function resolveManagedSendArgv(argv) {
|
|
|
3217
3522
|
file = { filePath: values.get('--file'), as, contentType: values.get('--content-type'), text: values.get('--text'), transcript: values.get('--transcript') };
|
|
3218
3523
|
}
|
|
3219
3524
|
else if (values.has('--payload')) {
|
|
3220
|
-
payload =
|
|
3221
|
-
if (!payload || typeof payload !== 'object' || Array.isArray(payload))
|
|
3222
|
-
throw new Error('--payload must be a JSON object');
|
|
3525
|
+
payload = parseJsonObjectArgument(values.get('--payload'));
|
|
3223
3526
|
}
|
|
3224
3527
|
else if (values.has('--link')) {
|
|
3225
3528
|
assertEcSingleLine(values.get('--title'), '--title');
|
|
@@ -3235,9 +3538,9 @@ export function resolveManagedSendArgv(argv) {
|
|
|
3235
3538
|
throw new Error('Attachment options require --file');
|
|
3236
3539
|
if (booleans.has('--mention-all'))
|
|
3237
3540
|
mentions.push({ scope: 'all' });
|
|
3238
|
-
return { aid, to, scope, payload, file, textFile: values.get('--text-from-file'),
|
|
3541
|
+
return { aid, to, scope, payload, rawPayload: values.has('--payload'), file, textFile: values.get('--text-from-file'),
|
|
3239
3542
|
encrypt: booleans.has('--no-encrypt') ? false : booleans.has('--encrypt') ? true : undefined,
|
|
3240
|
-
thread, checkEvery, checkEveryMs: checkEveryMs ?? undefined, triggerPromptExtra, returnPolicy: returnPolicy, handoffContext,
|
|
3543
|
+
thread, checkEvery, checkEveryMs: checkEveryMs ?? undefined, triggerPromptExtra, returnPolicy: returnPolicy, timeoutMs, handoffContext,
|
|
3241
3544
|
mentions: mentions.length ? mentions : undefined };
|
|
3242
3545
|
}
|
|
3243
3546
|
/**
|
|
@@ -3259,7 +3562,8 @@ const VALUE_FLAGS = new Set([
|
|
|
3259
3562
|
'--as', '--content-type', '--mention', '--visibility', '--join-mode',
|
|
3260
3563
|
'--group-id', '--name', '--message', '--answer', '--page', '--size',
|
|
3261
3564
|
'--member-type',
|
|
3262
|
-
'--aun-path', '--thread', '--handoff-context',
|
|
3565
|
+
'--aun-path', '--thread', '--handoff-context', '--self',
|
|
3566
|
+
'--return', '--timeout',
|
|
3263
3567
|
// 跨话题发送的复查参数:漏登记会让 collectPositional 把它们当正文(正文污染)
|
|
3264
3568
|
'--check-every', '--trigger-prompt',
|
|
3265
3569
|
]);
|
|
@@ -3290,17 +3594,6 @@ function collectPositional(args, startIdx, extraValueFlags) {
|
|
|
3290
3594
|
}
|
|
3291
3595
|
return out;
|
|
3292
3596
|
}
|
|
3293
|
-
function parseMsgHistoryTime(value) {
|
|
3294
|
-
const trimmed = value.trim();
|
|
3295
|
-
if (!trimmed)
|
|
3296
|
-
return undefined;
|
|
3297
|
-
if (/^-?\d+$/.test(trimmed)) {
|
|
3298
|
-
const timestamp = Number(trimmed);
|
|
3299
|
-
return Number.isSafeInteger(timestamp) ? timestamp : undefined;
|
|
3300
|
-
}
|
|
3301
|
-
const timestamp = Date.parse(trimmed);
|
|
3302
|
-
return Number.isFinite(timestamp) ? timestamp : undefined;
|
|
3303
|
-
}
|
|
3304
3597
|
/** Resolve a configured channel instance without exposing Agent session paths. */
|
|
3305
3598
|
function resolveHistoryChannelName(agentAid, channel, requested) {
|
|
3306
3599
|
if (channel === 'aun' && requested === undefined)
|