@cteno/sdk-linux-x64 0.0.0-stage → 2.10.14
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/LICENSE.daemon +31 -0
- package/README.md +4 -2
- package/agents/flow-analyzer/AGENT.md +71 -0
- package/bin/cteno-host-cli +4 -0
- package/bin/cteno-wiki +0 -0
- package/bin/rg +0 -0
- package/bin/secret-exec +0 -0
- package/manifest.json +12 -0
- package/package.json +33 -4
- package/plugins/agent-secrets/.cteno-plugin/plugin.json +133 -0
- package/plugins/agent-secrets/bin/linux-x64/fnox +4 -0
- package/plugins/agent-secrets/bin/linux-x64/secret-exec +0 -0
- package/plugins/agent-secrets/skills/secret/SKILL.md +201 -0
- package/plugins/cteno.browser/.cteno-plugin/plugin.json +65 -0
- package/plugins/cteno.browser/skills/browser/SKILL.md +28 -0
- package/plugins/cteno.inbox/.cteno-plugin/plugin.json +132 -0
- package/plugins/cteno.inbox/bin/all/cteno-connector-credential +31 -0
- package/plugins/cteno.inbox/bin/all/cteno-telegram-connector +33 -0
- package/plugins/cteno.inbox/bin/linux-x64/cteno-inbox +0 -0
- package/plugins/cteno.inbox/skills/cteno-inbox/SKILL.md +374 -0
- package/plugins/cteno.inbox/tools/inbox_focus/TOOL.md +44 -0
- package/plugins/cteno.memory/.cteno-plugin/plugin.json +176 -0
- package/plugins/cteno.memory/agents/llm-wiki/AGENT.md +96 -0
- package/plugins/cteno.memory/bin/linux-x64/cteno-wiki +0 -0
- package/plugins/cteno.memory/skills/codebase-wiki/SKILL.md +52 -0
- package/plugins/cteno.memory/skills/cteno.memory.native/SKILL.md +110 -0
- package/plugins/cteno.memory/tools/memory/TOOL.md +492 -0
- package/plugins/cteno.memory/ui/index.html +822 -0
- package/plugins/cteno.models/.cteno-plugin/plugin.json +191 -0
- package/plugins/cteno.websearch/.cteno-plugin/plugin.json +45 -0
- package/plugins/jev/.cteno-plugin/plugin.json +309 -0
- package/plugins/jev/skills/jev-browser/SKILL.md +66 -0
- package/plugins/jev/skills/jev-browser/jev_browser.py +335 -0
- package/skills/agent-create/SKILL.md +86 -0
- package/skills/cteno-host-cli/SKILL.md +256 -0
- package/skills/docx/LICENSE +23 -0
- package/skills/docx/NOTICE.md +8 -0
- package/skills/docx/SKILL.md +69 -0
- package/skills/docx/references/cjk_typography.md +357 -0
- package/skills/docx/references/design_principles.md +819 -0
- package/skills/docx/references/revisions-and-comments.md +88 -0
- package/skills/docx/references/track_changes_guide.md +200 -0
- package/skills/docx/references/typography_guide.md +294 -0
- package/skills/docx/scripts/cjk_fonts.py +244 -0
- package/skills/docx/scripts/docx_comments.py +289 -0
- package/skills/docx/scripts/docx_common.py +92 -0
- package/skills/docx/scripts/docx_create.py +259 -0
- package/skills/docx/scripts/docx_edit.py +250 -0
- package/skills/docx/scripts/docx_read.py +149 -0
- package/skills/docx/scripts/docx_revisions.py +146 -0
- package/skills/docx/scripts/docx_template.py +70 -0
- package/skills/docx/scripts/docx_validate.py +156 -0
- package/skills/docx/scripts/render.py +250 -0
- package/skills/docx/tests/test_docx_skill.py +525 -0
- package/skills/home-devices/SKILL.md +121 -0
- package/skills/mcp/SKILL.md +72 -0
- package/skills/mcp/scripts/mcp_setup.py +439 -0
- package/skills/pdf/LICENSE +23 -0
- package/skills/pdf/NOTICE.md +8 -0
- package/skills/pdf/SKILL.md +63 -0
- package/skills/pdf/references/design-tokens.md +381 -0
- package/skills/pdf/references/forms.md +99 -0
- package/skills/pdf/references/nano-pdf-editing.md +40 -0
- package/skills/pdf/references/ocr-extraction.md +165 -0
- package/skills/pdf/scripts/_raster.py +76 -0
- package/skills/pdf/scripts/extract_marker.py +88 -0
- package/skills/pdf/scripts/extract_pymupdf.py +101 -0
- package/skills/pdf/scripts/pdf_create.py +191 -0
- package/skills/pdf/scripts/pdf_fill_form.py +97 -0
- package/skills/pdf/scripts/pdf_form_layout.py +168 -0
- package/skills/pdf/scripts/pdf_make_form.py +145 -0
- package/skills/pdf/scripts/pdf_merge.py +50 -0
- package/skills/pdf/scripts/pdf_meta.py +115 -0
- package/skills/pdf/scripts/pdf_page_image.py +99 -0
- package/skills/pdf/scripts/pdf_read.py +153 -0
- package/skills/pdf/scripts/pdf_secure.py +71 -0
- package/skills/pdf/scripts/pdf_split.py +84 -0
- package/skills/pdf/scripts/pdf_stamp.py +143 -0
- package/skills/pdf/scripts/pdf_watermark.py +51 -0
- package/skills/pdf/scripts/render.py +250 -0
- package/skills/pdf/tests/test_pdf_skill.py +415 -0
- package/skills/pptx/LICENSE +23 -0
- package/skills/pptx/NOTICE.md +8 -0
- package/skills/pptx/SKILL.md +62 -0
- package/skills/pptx/references/design-system.md +392 -0
- package/skills/pptx/references/pptxgenjs.md +420 -0
- package/skills/pptx/references/slide-types.md +413 -0
- package/skills/pptx/scripts/cjk_fonts.py +244 -0
- package/skills/pptx/scripts/pptx_create.py +240 -0
- package/skills/pptx/scripts/pptx_edit.py +436 -0
- package/skills/pptx/scripts/pptx_from_template.py +88 -0
- package/skills/pptx/scripts/pptx_read.py +131 -0
- package/skills/pptx/scripts/pptx_render.py +96 -0
- package/skills/pptx/scripts/render.py +250 -0
- package/skills/pptx/tests/test_powerpoint_skill.py +477 -0
- package/skills/skill-create/SKILL.md +114 -0
- package/skills/video-generation/SKILL.md +27 -0
- package/skills/xlsx/LICENSE +23 -0
- package/skills/xlsx/NOTICE.md +8 -0
- package/skills/xlsx/SKILL.md +63 -0
- package/skills/xlsx/references/format.md +71 -0
- package/skills/xlsx/references/restructuring.md +71 -0
- package/skills/xlsx/scripts/csv_to_xlsx.py +110 -0
- package/skills/xlsx/scripts/formula_check.py +428 -0
- package/skills/xlsx/scripts/render.py +250 -0
- package/skills/xlsx/scripts/xlsx_create.py +265 -0
- package/skills/xlsx/scripts/xlsx_edit.py +269 -0
- package/skills/xlsx/scripts/xlsx_read.py +166 -0
- package/skills/xlsx/scripts/xlsx_recalc.py +119 -0
- package/skills/xlsx/scripts/xlsx_restructure.py +343 -0
- package/skills/xlsx/scripts/xlsx_to_csv.py +75 -0
- package/skills/xlsx/tests/test_xlsx_skill.py +544 -0
- package/tools/create_goal/TOOL.md +36 -0
- package/tools/image_generation/TOOL.md +81 -0
- package/tools/skill/TOOL.md +117 -0
- package/tools/update_profile_notes/TOOL.md +39 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 2,
|
|
3
|
+
"id": "cteno.inbox",
|
|
4
|
+
"version": "0.6.4",
|
|
5
|
+
"title": "Cteno Connector",
|
|
6
|
+
"icon": "git-network-outline",
|
|
7
|
+
"color": "#276EF1",
|
|
8
|
+
"developer": "Cteno",
|
|
9
|
+
"category": "消息接入",
|
|
10
|
+
"featured": true,
|
|
11
|
+
"origin": "builtin",
|
|
12
|
+
"description": "集中管理多平台 Bot Connector:从本机平台 CLI 导入 Bot 实例,统一负责监听、Session 绑定和云端迁移;平台 CLI 插件保持上游原生边界。",
|
|
13
|
+
"defaultEnabled": true,
|
|
14
|
+
"dependencies": [
|
|
15
|
+
"cteno.connector-gateway"
|
|
16
|
+
],
|
|
17
|
+
"provides": {
|
|
18
|
+
"bin": [
|
|
19
|
+
"cteno-inbox"
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
"surfaces": {
|
|
23
|
+
"prompt": {
|
|
24
|
+
"fragments": [
|
|
25
|
+
"skills/cteno-inbox/SKILL.md"
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
"tools": [
|
|
29
|
+
{
|
|
30
|
+
"id": "inbox_focus",
|
|
31
|
+
"definition": "tools/inbox_focus/TOOL.md"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"rpc": {
|
|
35
|
+
"handler": {
|
|
36
|
+
"type": "command",
|
|
37
|
+
"command": "cteno-inbox",
|
|
38
|
+
"args": [
|
|
39
|
+
"rpc"
|
|
40
|
+
],
|
|
41
|
+
"timeout_secs": 10
|
|
42
|
+
},
|
|
43
|
+
"methods": [
|
|
44
|
+
"focus.compose",
|
|
45
|
+
"focus.get",
|
|
46
|
+
"focus.set"
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
"hooks": [
|
|
50
|
+
{
|
|
51
|
+
"event": "PreConnectorDeliver",
|
|
52
|
+
"handler": {
|
|
53
|
+
"type": "decision",
|
|
54
|
+
"point": "inbox.smart-route",
|
|
55
|
+
"version": 7,
|
|
56
|
+
"mode": "active",
|
|
57
|
+
"title": "route=decision:投递前问决策模型 wake / note / ignore",
|
|
58
|
+
"skipIf": "!/decide",
|
|
59
|
+
"timeoutSecs": 15,
|
|
60
|
+
"maxFieldChars": 16384,
|
|
61
|
+
"state": "Source: {{/source}} / {{/roomName}}\nAddressed to the bot (@-mention or direct message): {{/isMention}}\nWhat the owner cares about right now (decide against this): {{rpc:/data/focus}}\n\nNew message (platform raw event JSON; read the text and sender inside it):\n{{/raw}}",
|
|
62
|
+
"questions": {
|
|
63
|
+
"route": {
|
|
64
|
+
"type": "choice",
|
|
65
|
+
"instructions": "A bot reads messages from this source before they reach its owner. Decide what it should do with the new message, judged against what the owner cares about. A message addressed to the bot (@-mention or direct message) leans towards wake, but the owner's focus decides: when it clearly falls outside what the owner wants handled, or is spam, ads, promotions or harassment, answer ignore even for a direct message.",
|
|
66
|
+
"options": {
|
|
67
|
+
"wake": "Someone wants something now: a question, a request, a complaint or bug report, or a call to the bot or the owner, matching what the owner wants handled",
|
|
68
|
+
"note": "An announcement or status update nobody needs answered: a meeting time, a decision made, a result, a new account or link, a plan. Worth remembering, nothing to do now",
|
|
69
|
+
"ignore": "Spam, ads, promotions, scams or harassment (even when it @-mentions the bot); or small talk, greetings, thanks, acknowledgements (ok, 收到), emoji, jokes, or anything unrelated to the owner's interests",
|
|
70
|
+
"none": "None of the other options fits this case"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"actions": {
|
|
75
|
+
"route": {
|
|
76
|
+
"wake": {
|
|
77
|
+
"decision": "allow",
|
|
78
|
+
"additionalContext": "[决策模型判定需要处理;是否 @ 你见事件 isMention,回不回复按本群规则自己判断]",
|
|
79
|
+
"minConfidence": 0.6,
|
|
80
|
+
"otherwise": {
|
|
81
|
+
"decision": "note"
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"note": {
|
|
85
|
+
"decision": "note"
|
|
86
|
+
},
|
|
87
|
+
"none": null,
|
|
88
|
+
"ignore": {
|
|
89
|
+
"decision": "deny",
|
|
90
|
+
"reason": "决策模型判定为闲聊",
|
|
91
|
+
"minConfidence": 0.6,
|
|
92
|
+
"otherwise": {
|
|
93
|
+
"decision": "note"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"stateRpc": {
|
|
99
|
+
"method": "plugin-rpc",
|
|
100
|
+
"params": {
|
|
101
|
+
"pluginId": "cteno.inbox",
|
|
102
|
+
"method": "focus.compose",
|
|
103
|
+
"params": {
|
|
104
|
+
"sessionId": "{{/sessionId}}",
|
|
105
|
+
"roomFocus": "{{/roomFocus}}"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"event": "UserPromptSubmit",
|
|
113
|
+
"handler": {
|
|
114
|
+
"type": "command",
|
|
115
|
+
"command": "cteno-inbox",
|
|
116
|
+
"args": [
|
|
117
|
+
"hook",
|
|
118
|
+
"prompt-context"
|
|
119
|
+
],
|
|
120
|
+
"timeoutSecs": 2
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
},
|
|
125
|
+
"permissions": {
|
|
126
|
+
"externalProcess": [
|
|
127
|
+
"cteno-inbox"
|
|
128
|
+
],
|
|
129
|
+
"network": true,
|
|
130
|
+
"filesystem": []
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
set -eu
|
|
3
|
+
|
|
4
|
+
# Connector-owned formatter for structured Bot credential bundles imported by a
|
|
5
|
+
# Platform Skill. Host injects the selected JSON only into this process; it is
|
|
6
|
+
# never accepted in argv or read from a platform CLI's private current profile.
|
|
7
|
+
case "${1:-}" in
|
|
8
|
+
credential-json)
|
|
9
|
+
[ -n "${CTENO_CONNECTOR_CREDENTIAL_JSON:-}" ] || {
|
|
10
|
+
printf '%s\n' '[cteno-connector-credential] credential JSON is missing' >&2
|
|
11
|
+
exit 1
|
|
12
|
+
}
|
|
13
|
+
if command -v jq >/dev/null 2>&1; then
|
|
14
|
+
printf '%s' "$CTENO_CONNECTOR_CREDENTIAL_JSON" | jq -ce 'select(type == "object")'
|
|
15
|
+
else
|
|
16
|
+
# Gateway performs JSON parsing too, but without jq we cannot safely prove
|
|
17
|
+
# this is an object without echoing malformed secret material.
|
|
18
|
+
printf '%s\n' '[cteno-connector-credential] jq is required' >&2
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
;;
|
|
22
|
+
no-credential)
|
|
23
|
+
# Sources whose login lives somewhere the Host already owns (e.g. the
|
|
24
|
+
# browser engine profile for the generic web-watch driver) have no bundle.
|
|
25
|
+
printf '%s' '{}'
|
|
26
|
+
;;
|
|
27
|
+
*)
|
|
28
|
+
printf '%s\n' 'usage: cteno-connector-credential credential-json|no-credential' >&2
|
|
29
|
+
exit 2
|
|
30
|
+
;;
|
|
31
|
+
esac
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
set -eu
|
|
3
|
+
|
|
4
|
+
# Connector-owned Telegram credential formatter. The Host injects the selected
|
|
5
|
+
# Bot token only into this process as TG_BOT_TOKEN. The token is never accepted
|
|
6
|
+
# in argv and this helper never reads Telegram's legacy singleton vault key.
|
|
7
|
+
case "${1:-}" in
|
|
8
|
+
credential-token)
|
|
9
|
+
[ -n "${TG_BOT_TOKEN:-}" ] || {
|
|
10
|
+
printf '%s\n' '[cteno-telegram-connector] TG_BOT_TOKEN is missing' >&2
|
|
11
|
+
exit 1
|
|
12
|
+
}
|
|
13
|
+
API="${CTENO_TELEGRAM_API_BASE:-https://api.telegram.org}"
|
|
14
|
+
PROXY="${TG_BOT_PROXY:-}"
|
|
15
|
+
if command -v jq >/dev/null 2>&1; then
|
|
16
|
+
jq -cn --arg botToken "$TG_BOT_TOKEN" --arg apiBase "$API" --arg proxy "$PROXY" \
|
|
17
|
+
'{botToken:$botToken,apiBase:$apiBase,proxy:$proxy}'
|
|
18
|
+
else
|
|
19
|
+
# Tokens cannot contain JSON metacharacters in Telegram's Bot API format;
|
|
20
|
+
# still reject unexpected values rather than implementing unsafe escaping.
|
|
21
|
+
case "$TG_BOT_TOKEN$API$PROXY" in
|
|
22
|
+
*'"'*|*'\\'*|*'
|
|
23
|
+
'*) exit 1 ;;
|
|
24
|
+
esac
|
|
25
|
+
printf '{"botToken":"%s","apiBase":"%s","proxy":"%s"}\n' \
|
|
26
|
+
"$TG_BOT_TOKEN" "$API" "$PROXY"
|
|
27
|
+
fi
|
|
28
|
+
;;
|
|
29
|
+
*)
|
|
30
|
+
printf '%s\n' 'usage: cteno-telegram-connector credential-token' >&2
|
|
31
|
+
exit 2
|
|
32
|
+
;;
|
|
33
|
+
esac
|
|
Binary file
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cteno-inbox
|
|
3
|
+
description: Cteno Connector 的唯一管理 Skill:使用本机已登录的平台 CLI 管理多 Bot 身份,并创建、监听、绑定和迁移 Connector 实例。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Cteno Connector
|
|
7
|
+
|
|
8
|
+
> **迁移功能暂时停用**:Bot/Session、本机/云端迁移、export/import、recall、ownership clear 均不得执行;以下迁移章节仅保留历史设计,不是可用能力。不要通过 shell、远程RPC或旧客户端绕过。已有迁移记录保持原样,不恢复、不清锁、不动凭证。
|
|
9
|
+
|
|
10
|
+
本 Skill 是 Cteno 中唯一的 Connector 管理入口。当前物理边界是:
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
平台 CLI plugin Cteno Connector plugin(cteno.inbox)
|
|
14
|
+
├─ 尽量原样打包上游 CLI ├─ 四平台 receive driver / Gateway runtime
|
|
15
|
+
├─ 同步上游 Platform Skill ├─ Connector templates 与实例 store
|
|
16
|
+
└─ user/bot 平台操作 ├─ Bot credential adapter / secret broker
|
|
17
|
+
├─ source → Session binding
|
|
18
|
+
└─ 本地/云端监听权迁移
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
平台 CLI plugin 不再声明 `messageSources`,也不负责 daemon reconcile。Connector plugin 通过平台 Skill发现或创建 Bot;平台 Skill把凭据私下保存为一个实例级 vault key,再只把 key 名(不是 `secret://` URI 或明文)交回本 Skill。平台 CLI 后续升级、切换 current profile 或退出 user OAuth,不得改变已导入 Connector 的监听身份。
|
|
22
|
+
|
|
23
|
+
当前 Host 已实现:四个平台集中 account template、显式 `driver`、稳定 `externalId`、实例级 `credentialKey`、私有进程环境注入,以及列表结果的 credential handle 脱敏。旧静态平台 listener 已删除。
|
|
24
|
+
|
|
25
|
+
## 一、统一心智模型
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
本机平台 CLI 的 User/Profile
|
|
29
|
+
│ 创建、选择、管理
|
|
30
|
+
▼
|
|
31
|
+
平台 Bot Identity(可有 N 个)
|
|
32
|
+
│ 一次性导入
|
|
33
|
+
▼
|
|
34
|
+
Connector Instance(Cteno 自己持久化)
|
|
35
|
+
│ 监听权唯一
|
|
36
|
+
▼
|
|
37
|
+
0..1 Session(当前 source 单归属;Session 可挂 N sources)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
必须区分:
|
|
41
|
+
|
|
42
|
+
- **CLI profile/user**:平台管理上下文,不是 Connector runtime identity。
|
|
43
|
+
- **Bot external ID**:平台稳定身份,用于查重;不能用可变名称代替。
|
|
44
|
+
- **Connector ID/source ID**:Cteno 稳定主键,改名、轮换凭据、移动 Session 时不变。
|
|
45
|
+
- **Bot credential**:导入后归 Connector instance;不得继续依赖 CLI 的“当前选择”。
|
|
46
|
+
- **display name**:只展示,不参与唯一性或 secret namespace。
|
|
47
|
+
|
|
48
|
+
## 二、唯一允许的 Bot Connector 建立流程
|
|
49
|
+
|
|
50
|
+
必须严格按以下顺序执行;模板不是 source,未创建实例前禁止调用 `inbox
|
|
51
|
+
resume <templateId>`:
|
|
52
|
+
|
|
53
|
+
1. 激活对应平台 Skill,只读列出本机 CLI 已登录的全部 user/profile 及其
|
|
54
|
+
Bot inventory;展示名称与稳定 external ID。
|
|
55
|
+
2. **停止并让用户选择一个 Bot**。不得默认第一个、current/default Bot,
|
|
56
|
+
不得因为 CLI status 显示 configured 就视为 Connector 已创建。
|
|
57
|
+
3. 若用户选择创建新 Bot,先确认平台写操作;平台 Skill 必须先展示“消息基础能力 + 用户明确选择的文档/日历/通讯录/任务/会议等可选能力”,区分 Bot 应用权限、用户委托授权和具体资源成员权限,再通过官方 CLI 的可见确认/扫码/浏览器流程申请并验证。不得默认全量权限,不得编造 scope,也不得把 user OAuth 当成 Connector credential。随后由平台 Skill 创建/发布。
|
|
58
|
+
4. 对选定 Bot **只调用平台 Skill 指定的 private credential importer**,把完整的 driver-native JSON credential bundle 保存到实例级 vault key;不得自行调用平台原始 credential 命令,不得用 `secret-exec import` 拆成多个字段,也不得把单独 token/AppSecret 字符串当作 `--credential-key` 对应的值。推荐 key namespace 为 `cteno/connectors/<sourceId>/credential-v<N>`。明文不得进入 transcript、argv、日志或普通 JSON 结果。
|
|
59
|
+
5. private importer 成功后,只使用它返回的 `credentialKey` 创建或复用 durable instance:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
cteno-host-cli inbox subscribe \
|
|
63
|
+
--plugin cteno.inbox \
|
|
64
|
+
--template <dingtalk|feishu|wecom|telegram> \
|
|
65
|
+
--account <agent 派生> \
|
|
66
|
+
--external-id <botExternalId> \
|
|
67
|
+
--credential-key <vaultKeyName> \
|
|
68
|
+
--id <agent 派生> \
|
|
69
|
+
--name <agent 默认名>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**这一步不要问用户任何问题。** 登录/扫码完成时,平台 Skill 已经返回了
|
|
73
|
+
`botExternalId`,agent 由此可以定出全部参数:
|
|
74
|
+
|
|
75
|
+
- `--external-id`:平台 Skill 返回的 Bot 真实身份,查重唯一依据
|
|
76
|
+
- `--account` / `--id`:由 `botExternalId` 派生。取其 **local-part 全量**
|
|
77
|
+
(剥掉 `@im.bot` 之类域后缀),全小写、只留 `[a-z0-9-]`,得到
|
|
78
|
+
`wx-f1dc5d0aaec3` / `weixin-f1dc5d0aaec3` 这样的值。
|
|
79
|
+
- `--name`:直接给平台默认名(如「微信助手」)。事后用
|
|
80
|
+
`cteno-host-cli inbox rename` 可改,无需事前确认。
|
|
81
|
+
|
|
82
|
+
⚠️ **不要截断 external id 取前缀。** 同一用户可以接入同平台的多个 Bot,
|
|
83
|
+
截断后的短前缀存在碰撞可能;而 `id` 碰撞**不会报错**——host 的
|
|
84
|
+
duplicate guard 条件是 `e.id != id`(`inbox_worker.rs:877`),同 id 会走
|
|
85
|
+
复用路径**静默覆盖**掉前一个实例。`external_id` 冲突才会被拒。因此
|
|
86
|
+
`id` 必须由完整 local-part 派生,且**不要**掺时间戳或随机数(同一 Bot
|
|
87
|
+
重复接入应得到同一 `sourceId`,否则会产生僵尸实例)。
|
|
88
|
+
|
|
89
|
+
`--account` 已退化为展示 slug、不再承担 Bot identity;`--id` 是内部不可变
|
|
90
|
+
主键(架构建议 UUID);查重按 `plugin + template + external_id` 判定,与
|
|
91
|
+
这两个值无关。让用户为内部键取名既无收益,又制造"取错不可改"的心理负担。
|
|
92
|
+
见 `docs/architecture/platform-cli-connector-decoupling.md`。
|
|
93
|
+
|
|
94
|
+
`--id` 在 CLI 中是可选的;但**必须显式传**,否则拿不到确定的 sourceId
|
|
95
|
+
去做后续 `inbox status` 核验与用户挂载指引。
|
|
96
|
+
|
|
97
|
+
创建成功后连同显示名一起告知用户,并说明可用 `inbox rename` 改名。
|
|
98
|
+
|
|
99
|
+
6. 用 `inbox list` 核验 external ID 和 `credentialConfigured=true`,再用
|
|
100
|
+
`inbox status` 核验这个**精确 sourceId** 达到 `receiving`。
|
|
101
|
+
7. 挂载:**一个 Connector 只挂一个对话,一个对话可以挂多个 Connector**。未挂载的 source 收到事件会被**拒收**——不新建任何 Session,错误前缀 `connector source is unbound`,`inbox rooms <source>` 显示 `bound=false` 并累计 `unboundRejectedEvents`;所以创建完必须挂载。**挂到哪里由用户决定,Agent 不得替用户默认**(2026-10-27 用户纠正:在主 Jelly 里让它建一个企业微信客户群 Bot,那个 Bot 应该有自己的新 Jelly,而不是占用当前对话)。用户在请求里已经说明挂到哪时直接照做;否则达到 receiving 后用 `decision` 工具(mode: choice)问一次,选项固定为:
|
|
102
|
+
- **「新开一个 Jelly 专门负责(推荐)」** → `cteno-host-cli inbox bind <sourceId> --new-jelly --name <连接器/Bot 显示名>`:新建 Jelly 并挂到它的主对话,结果含 `newJelly { id, mainThreadId }`;当前对话不变;挂载失败时新 Jelly 会被自动删掉。旧 CLI 不认识 `--new-jelly` 时:`cteno-host-cli jelly create --name <显示名>` → 取 `jelly.mainThreadId` → `inbox bind <sourceId> <mainThreadId>`。当前对话是用户的主 Jelly 或正忙于别的事时,这个选项尤其应放第一位。
|
|
103
|
+
- **「挂到当前对话」** → `cteno-host-cli inbox bind <sourceId>`(sessionId 省略即取 `$CTENO_SESSION_ID`)。
|
|
104
|
+
- **「挂到其他对话」** → 再问用户是哪个对话,然后 `inbox bind <sourceId> <sessionId>`。
|
|
105
|
+
|
|
106
|
+
Agent 不得自行列出/挑选其它 Session。目标对话若还不属于任何 Jelly,daemon 会把**它本身**原地升级为 Jelly(同一 session、保留历史和目录,结果里 `jelly.outcome=promoted|created`),以便工作台和 Jelly 两种模式都能看到;已是 Jelly 主对话或 Jelly 线程则不变(`already_jelly` / `skipped_jelly_thread`)。重绑到另一个对话会把挂载整体移走(旧对话名下的本人渠道路由一并释放)。挂载后用 `inbox rooms <sourceId>` 读回 `bound=true` 与 `mountSessionId`。
|
|
107
|
+
8. 后续仅在目标 Session 已由用户手动挂载并收到真实消息后,证明平台 CLI/helper reply/send 使用与该 Connector 相同的 Bot identity。无法证明时报告 outbound 缺口,不得回退默认 Bot。
|
|
108
|
+
|
|
109
|
+
硬禁令:不得修改、软链或覆盖已安装插件目录;不得用平台旧 helper 的
|
|
110
|
+
singleton `status/configure` 代替 durable instance 导入;不得打印 credential
|
|
111
|
+
来“测试”。缺少私有 importer 时应明确报告实现缺口。
|
|
112
|
+
|
|
113
|
+
### 平台多账号 / 多 Bot 知识必须委托给对应 Skill
|
|
114
|
+
|
|
115
|
+
本 Skill **不维护** DingTalk、Feishu、WeCom、Telegram 的 profile、目录、命令或 Bot 创建细节。它只负责激活对应 IM plugin Skill,并消费该 Skill 给出的非敏感选择结果。
|
|
116
|
+
|
|
117
|
+
每个 IM plugin Skill 必须自行封装:
|
|
118
|
+
|
|
119
|
+
- 如何列出本机 CLI 的 user/profile/account;
|
|
120
|
+
- 如何在不污染全局 current/default 的情况下选择一个账号;
|
|
121
|
+
- 如何列出、创建、发布、轮换该账号下的 N 个 Bot;
|
|
122
|
+
- 每条 CLI 命令如何显式重选同一 profile/config-dir/Bot;
|
|
123
|
+
- 哪些字段是稳定 external ID,哪些只是显示名;
|
|
124
|
+
- 如何把 credential 私下交给 Connector importer;
|
|
125
|
+
- raw event 如何解析,以及用正确 Bot 实例 reply/send/download。
|
|
126
|
+
|
|
127
|
+
平台 Skill 完成选择后,向本 Skill提供以下**非敏感 handoff**:
|
|
128
|
+
|
|
129
|
+
```text
|
|
130
|
+
PlatformBotSelection
|
|
131
|
+
platform
|
|
132
|
+
botExternalId
|
|
133
|
+
displayName
|
|
134
|
+
connectSkill
|
|
135
|
+
cliLocator # opaque metadata,由平台 Skill解释;不含 secret
|
|
136
|
+
credentialKey # private importer 返回的 key 名;其值是完整 driver JSON bundle,不是单独 token/AppSecret
|
|
137
|
+
metadata # robotCode/botOpenId 等公开平台字段
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
通用 Connector Skill不得自行解释 `cliLocator`,也不得复制平台命令。它只做:按 `platform + botExternalId` 查重、创建/更新 Connector instance、检查 health、绑定 Session、迁移监听权。
|
|
141
|
+
|
|
142
|
+
执行规则:
|
|
143
|
+
|
|
144
|
+
1. 根据平台激活 `dingtalk`、`feishu`、`wecom`、`telegram` 或 `email` Skill。
|
|
145
|
+
2. 要求平台 Skill先只读列出账号和 Bot inventory,再由用户明确选择。
|
|
146
|
+
3. 创建远端 Bot、发布、轮换 credential 等平台写操作由平台 Skill完成,并遵守确认规则。
|
|
147
|
+
4. secret 不得进入对话、argv、日志或普通 JSON 工具结果;本 Skill只接收 private importer/handle。
|
|
148
|
+
5. 导入时按 `platform + botExternalId` 查重;已有实例走 update/rotate,不创建第二个 listener。
|
|
149
|
+
6. 若平台 Skill不能稳定重选该账号/Bot,或 Host 尚不能向可见终端传递它要求的 selector,停止并报告能力缺口,不要退回全局默认账号。
|
|
150
|
+
|
|
151
|
+
## 三、平台 handoff 的最低验收
|
|
152
|
+
|
|
153
|
+
无论平台内部采用 profile、config directory 还是 Bot token,交回 Connector 前都必须证明:
|
|
154
|
+
|
|
155
|
+
- 账号/Profile 定位明确;
|
|
156
|
+
- Bot external ID 稳定且已从真实 CLI/API 读取;
|
|
157
|
+
- 再次执行 status 时能显式命中同一个 Bot;
|
|
158
|
+
- 切换 CLI 的 global current/default 不会改变该选择结果;
|
|
159
|
+
- credential import 指向该 Bot,而不是“第一个”“当前”或默认 Bot;
|
|
160
|
+
- 平台 Skill已经记录后续 reply/send 如何选择同一个 Bot 实例。
|
|
161
|
+
|
|
162
|
+
创建实例使用真实 Host CLI:
|
|
163
|
+
|
|
164
|
+
```sh
|
|
165
|
+
cteno-host-cli inbox subscribe \
|
|
166
|
+
--plugin cteno.inbox \
|
|
167
|
+
--template <dingtalk|feishu|wecom|telegram> \
|
|
168
|
+
--account <agent 派生> \
|
|
169
|
+
--external-id <botExternalId> \
|
|
170
|
+
--credential-key <vaultKeyName> \
|
|
171
|
+
--id <agent 派生> \
|
|
172
|
+
--name <agent 默认名>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
`--account` / `--id` / `--name` 全部由 agent 从 `botExternalId` 定出,
|
|
176
|
+
不要问用户(派生规则见上文第 5 步)。
|
|
177
|
+
|
|
178
|
+
`--credential-key` 只允许安全的 key 名;不要传 `secret://vault/...`,因为 Agent shell 的通用 secret 注入可能在进程启动前解析 URI。Host 内部构造 handle,`inbox list` 的 `credentialConfigured=true` **只表示保存了 handle,不证明凭据内容或 JSON shape 正确**;必须以精确 sourceId 达到 `receiving` 作为导入成功判据。
|
|
179
|
+
|
|
180
|
+
## 四、Connector plugin 的集中化实现
|
|
181
|
+
|
|
182
|
+
### 1. Plugin 集中声明模板
|
|
183
|
+
|
|
184
|
+
`cteno.inbox` manifest 集中声明四个平台模板,平台 CLI plugin 不再声明 listener:
|
|
185
|
+
|
|
186
|
+
```text
|
|
187
|
+
dingtalk → driver dingtalk, connectSkill dingtalk
|
|
188
|
+
feishu → driver feishu, connectSkill feishu
|
|
189
|
+
wecom → driver wecom, connectSkill wecom
|
|
190
|
+
telegram → driver telegram, connectSkill telegram
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
所有模板都走 `instantiation:"account"`,实例复用现有 `InboxSubscription` → `HostPluginMessageSource` → reconcile 链。Host 已用独立 `driver` 选择 Gateway 实现,`pluginId=cteno.inbox` 只表示生命周期所有者;`connectSkill` 仍指平台 Skill。
|
|
194
|
+
|
|
195
|
+
### 2. Connector plugin 自带的代码
|
|
196
|
+
|
|
197
|
+
`cteno.inbox` 集中拥有且已经实现:
|
|
198
|
+
|
|
199
|
+
- 四个平台的 `messageSources` account templates;
|
|
200
|
+
- durable `InboxSubscription` Connector instances;
|
|
201
|
+
- `externalId` 查重、实例级 `configJson` 与 credential handle;
|
|
202
|
+
- `cteno-telegram-connector` / `cteno-connector-credential` 私有 credential formatter;
|
|
203
|
+
- Host → Connector Gateway reconcile、health、raw event delivery;
|
|
204
|
+
- 唯一的 Connector lifecycle Skill。
|
|
205
|
+
|
|
206
|
+
平台 plugin 继续拥有:
|
|
207
|
+
|
|
208
|
+
- 尽量原样的上游 CLI binary;
|
|
209
|
+
- 多 user/profile/Bot 的 Platform Skill;
|
|
210
|
+
- user auth/probe;
|
|
211
|
+
- raw event 解释和 reply/send/download 等平台操作;
|
|
212
|
+
- WeCom 必须复用 live socket 时的窄平台 adapter(在独立发送路径被真实 E2E 证明前保留)。
|
|
213
|
+
|
|
214
|
+
平台 helper 可以继续物理存在于平台 plugin;关键不变量是它不再声明 listener、
|
|
215
|
+
不再依赖 Gateway,也不是 receive runtime 的 steady-state credential source。
|
|
216
|
+
|
|
217
|
+
### 3. 编排与依赖方向
|
|
218
|
+
|
|
219
|
+
```text
|
|
220
|
+
Agent
|
|
221
|
+
├─ 按需激活平台 Skill/CLI:inventory、创建、导入、平台操作
|
|
222
|
+
└─ 激活 cteno-inbox Skill/Host CLI:实例、监听、binding、迁移
|
|
223
|
+
|
|
224
|
+
已导入的 cteno.inbox Connector steady-state
|
|
225
|
+
-/→ 不调用平台 CLI plugin
|
|
226
|
+
-/→ 不读取平台 CLI 私有 current/profile/store
|
|
227
|
+
|
|
228
|
+
平台 CLI plugin
|
|
229
|
+
-/→ 不依赖 cteno.inbox
|
|
230
|
+
-/→ 不依赖 connector-gateway
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
不要让 `cteno.inbox` manifest 静态依赖所有平台 CLI plugin,否则用户仅使用 Telegram 也会被迫安装三套办公 CLI。Agent按任务组合两个独立能力面;已经导入的 Connector steady-state 不依赖平台 CLI 继续存在。
|
|
234
|
+
|
|
235
|
+
## 五、当前可用的 Host 生命周期
|
|
236
|
+
|
|
237
|
+
```sh
|
|
238
|
+
cteno-host-cli inbox templates
|
|
239
|
+
cteno-host-cli inbox list
|
|
240
|
+
cteno-host-cli inbox status
|
|
241
|
+
cteno-host-cli inbox subscribe \
|
|
242
|
+
--plugin cteno.inbox --template <platform> \
|
|
243
|
+
--account <label> --external-id <botId> --credential-key <vaultKey> \
|
|
244
|
+
[--id <sourceId>] [--name <displayName>]
|
|
245
|
+
cteno-host-cli inbox disconnect <sourceId>
|
|
246
|
+
cteno-host-cli inbox resume <sourceId>
|
|
247
|
+
cteno-host-cli inbox policy # 列出所有非默认投递策略
|
|
248
|
+
cteno-host-cli inbox policy <sourceId> # 读取某信源的投递策略
|
|
249
|
+
cteno-host-cli inbox policy <sourceId> queue|append|preempt # 设置(下一条事件即生效)
|
|
250
|
+
cteno-host-cli inbox rooms [<sourceId>] # 房间回读:绑定 × capture/route × 全量源健康
|
|
251
|
+
cteno-host-cli inbox room-policy <sourceId> <routeKey|*> [--capture mention|all] [--route direct|decision] [--focus <一句话>] [--name <群名>]
|
|
252
|
+
cteno-host-cli inbox bind <sourceId> [<sessionId>] [--owner-route <routeKey>]... # 省略 sessionId = 当前对话(先问用户挂哪)
|
|
253
|
+
cteno-host-cli inbox bind <sourceId> --new-jelly [--name <显示名>] # 新建 Jelly 并挂到它的主对话
|
|
254
|
+
cteno-host-cli inbox unbind <sourceId>
|
|
255
|
+
cteno-host-cli inbox remove <sourceId> # 只做权威预检
|
|
256
|
+
cteno-host-cli inbox remove <sourceId> --confirm <sourceId> # 永久删除本地 Connector Profile
|
|
257
|
+
cteno-host-cli inbox remove-subscription <subscriptionId> # 仅手写 generic subscription
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
当前限制:
|
|
261
|
+
|
|
262
|
+
- account-instantiated Connector 的 `disconnect` 会保留实例并进入可恢复
|
|
263
|
+
`paused`;只有 generic ad-hoc subscription 会被删除;
|
|
264
|
+
- durable Connector 的 `remove` 是状态丰富的两阶段永久删除,cloud-owned 时拒绝;本地删除不会自动删除厂商 Bot;
|
|
265
|
+
- 绑定服务是 source→对话的唯一真值:`inbox bind <source> [<sessionId>] [--owner-route <routeKey>]...`
|
|
266
|
+
/ `inbox unbind <source>`。旧的 `bind --room` / `unbind --room` 与 `room-policy --bind/--bind-session`
|
|
267
|
+
**已删除**,调用直接报 `was removed`,没有兼容层;
|
|
268
|
+
- **路由规则**(投递时由 Host 决定,Agent 不需要也不能逐房间绑定):
|
|
269
|
+
- **所有事件都进挂载对话本身**,Host 不按外部会话另开会话:挂载对话就是用户所有来源的统一收件与待办中心;
|
|
270
|
+
- 用户本人私聊 / 默认入口(无 routeKey)以及 `--owner-route` 声明的 routeKey → 视同用户本人在对话里说话;
|
|
271
|
+
- 群、别人的私聊、话题、邮件发件人等其它 routeKey → 带【外部会话】外框进挂载对话:标明来源,回复只能回该会话,
|
|
272
|
+
不得把用户私事或其它会话内容透露给它;需要用户拍板的直接在对话里问用户;
|
|
273
|
+
- 未挂载 → 拒收(见第二节第 7 步),不会自动新建 Session;
|
|
274
|
+
- 删除 Jelly 会经绑定服务清掉其所有会话(含链段)上的挂载与路由,相关 source 回到未挂载;
|
|
275
|
+
- `inbox policy` 决定**该 source 的事件如何进入已绑定 Session**,按 source 解析(多 source
|
|
276
|
+
fan-in 同一对话时各自独立):
|
|
277
|
+
- `queue`(默认):事件排队,等当前 turn 跑完再单独起一轮。默认必须是它,因为连接器
|
|
278
|
+
出站契约要求**逐事件回复**;把事件 B 插进正在处理事件 A 的 turn,模型很可能漏发某条上游回复;
|
|
279
|
+
- `append`:Session 正忙时把事件作为 steering 消息注入**当前 turn**,适合同一话题的追加补充;
|
|
280
|
+
- `preempt`:同样注入当前 turn,但语义上要求模型优先响应新事件,适合"别做了改做这个"的打断。
|
|
281
|
+
- 投递策略是**投递时现读**:改完下一条事件立即生效,**不重启 daemon、不 reconcile、不断开
|
|
282
|
+
平台长连接**(Bot 不会掉线)。因此 `inbox policy` 可以在 Session 正跑长 turn 的过程中改。
|
|
283
|
+
- 连接器迁移(inbox migrate)v1 一次只迁一个 source/挂载对话,并拒绝挂了多个 source 的对话;
|
|
284
|
+
- 旧静态 Bot source 不能凭 binding 自动还原 secret,必须由对应 Platform Skill明确选择 Bot 并导入一次。
|
|
285
|
+
|
|
286
|
+
## 五之二、群聊参与:两个开关 capture × route
|
|
287
|
+
|
|
288
|
+
每个群(`routeKey`,钉钉是 `chat:<openConversationId>`;`'*'` = 该连接器所有未单独设置的群的默认)有**两个正交**的开关:
|
|
289
|
+
|
|
290
|
+
| 开关 | 取值 | 含义 | 谁能开 |
|
|
291
|
+
|---|---|---|---|
|
|
292
|
+
| `capture` 收什么 | `mention`(默认)/ `all` | `mention` 只收 @Bot、回复 Bot、私聊;`all` 收该群全部消息 | 只有平台支持收非 @ 群消息时(manifest `groupMessages=all` 或伴随全量源 `ambientRunner`),否则 `room-policy` 报错 |
|
|
293
|
+
| `route` 怎么分流 | `direct`(默认)/ `decision` | `decision` = 每条收进来的消息(含 @、私聊、源级事件)先过**决策小模型**:需要处理的进会话起一轮(`wake`);有用的记下来随下一条带上(`note`,不起轮);无关的略过(`ignore`) | **任何平台**都能开 |
|
|
294
|
+
|
|
295
|
+
`--focus` 一句话说明什么该叫醒(不填用默认标准)。决策模型不可用时:非 @ 群消息**不投递**;@ / 私聊**照常投递**,
|
|
296
|
+
且决策判 ignore 的 @ 也只降级为 note、不丢。`inbox rooms` 显示 `decisionModelReady`、`smartToday`(route=decision 的房间)。
|
|
297
|
+
决策是本插件在 `PreConnectorDeliver` 上声明的 Hook(判定时刻 `inbox.smart-route`),`decision records --point inbox.smart-route`
|
|
298
|
+
可回看每条判定;用户想加自己的筛选规则就在同一事件上挂 Hook,不要改代码。
|
|
299
|
+
|
|
300
|
+
旧写法 `--tier mention|smart|active` 仍可用(`smart` = all+decision 但 @ 不过决策,`active` = all+direct),不要与
|
|
301
|
+
`--capture/--route` 混用;新设置一律用两轴。
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
cteno-host-cli inbox room-policy <sourceId> '*' --capture all --route decision --focus "客户报 bug、催进度、提到我时叫我"
|
|
305
|
+
cteno-host-cli inbox room-policy <sourceId> '*' --route decision # 只收 @,但每条 @ 也先过小模型(挡广告/骚扰)
|
|
306
|
+
cteno-host-cli inbox room-policy <sourceId> chat:<cid> --capture mention --route direct --name "客户群" # 切回
|
|
307
|
+
cteno-host-cli inbox rooms <sourceId> # 读回:capture / route / focus / smartToday
|
|
308
|
+
cteno-host-cli decision providers # route=decision 依赖的决策模型是否可用
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
接入连接器时主动问用户,一次问完:
|
|
312
|
+
1. **仅当平台支持收全部群消息**(`inbox rooms` 的 `fullGroupSupported=true`):“群里没 @ 机器人的消息要不要也收进来?”同意 → `--capture all`。
|
|
313
|
+
2. **所有平台都问**:“收进来的消息要不要先让小模型看一眼?需要处理的才叫醒你,有用的记下来,无关的略过(@ 你的消息除非明显是广告骚扰都会叫醒你)。”同意 → `--route decision`。
|
|
314
|
+
收全部却不过小模型(`all+direct`)费用高,一般建议两个都开。决策模型还没配置时先 `cteno-host-cli decision providers`
|
|
315
|
+
看状态,没有可用后端就如实说明“先直接投递、不做筛选”。
|
|
316
|
+
|
|
317
|
+
### 与对话的关系
|
|
318
|
+
|
|
319
|
+
房间不再单独绑定:`capture` 只决定哪些消息被收进来、`route` 只决定是否先过小模型,收进来之后一律进挂载对话。要让某个房间按
|
|
320
|
+
「用户本人说话」处理(例如用户本人的另一个私聊入口),在 `inbox bind` 时加 `--owner-route <routeKey>`。
|
|
321
|
+
|
|
322
|
+
硬规则:
|
|
323
|
+
|
|
324
|
+
- **关注点是对话的,不是群的**:route=decision 时决策模型判断“要不要叫醒”时,依据是这个挂载对话的关注点(工具 `inbox_focus`),对 IM、邮箱等所有挂在本对话上的连接器统一生效;房间 `--focus` 只是某个群的补充。用户说出新的关注(“以后发票的事叫我”“招聘先不用管了”“这周只看 A 客户”)时,先 `inbox_focus {action:get}` 读当前整段,合并成新的完整一段再 `inbox_focus {action:set, focus}`(是替换不是追加;保持几句话、写清要叫醒什么、不用管什么),然后用一句话告诉用户改成了什么。改完下一条消息就按新关注点判,不用重启。用户问“我之前关注什么”用 `inbox_focus {action:get, history:true}`。
|
|
325
|
+
- **只有 `capture=all` 需要平台支持收非 @ 群消息**:平台模板在 manifest 里声明了 `groupMessages: "all"` 或伴随全量源
|
|
326
|
+
(`messageSources[].ambientRunner`)才能设;否则 `room-policy` 直接报错 `does not declare native full-group support`,
|
|
327
|
+
不要反复重试,向用户说明该平台/当前权限只能 `capture=mention`(飞书、Telegram 等需要先在平台后台开群消息权限)。
|
|
328
|
+
`route=decision` 不受此限制。
|
|
329
|
+
- **伴随全量源随 capture 起停**:某个 source 第一个群切到 `capture=all` 时,Host 在 Connector Gateway 里
|
|
330
|
+
启动 `<sourceId>#ambient` 子进程(一个账号一个进程收所有群,Host 按 capture 筛选);第二个群
|
|
331
|
+
切 `all` 不重启;最后一个 `all` 群切回 `mention` 时停止。
|
|
332
|
+
- **全量源的登录状态看 `ambientHealth`**:`inbox rooms <source>` 的 `ambientHealth`/`ambientDetail`
|
|
333
|
+
单独反映全量源(例如钉钉需要 `dws auth login` 的用户身份)。它是 `error` 时 Bot 仍能正常回 @,
|
|
334
|
+
但非 @ 消息收不到——按 `ambientDetail` 的恢复提示让用户重新登录,不要去改 Bot 凭据。
|
|
335
|
+
- **不分会话**:所有房间共用挂载对话的上下文与 workdir/私有 memory。房间私事写进
|
|
336
|
+
`knowledge/rooms/<routeKey>.md`,靠页面路径分。
|
|
337
|
+
- **只回本群**:每条外部会话事件只能回到它自己的 `routeKey`,即使 payload 里出现别的会话 id。
|
|
338
|
+
- **已废弃**:`--context/--window/--ambient/--poll/--budget/--cooldown/--quiet` 与
|
|
339
|
+
`inbox identity`(门②)已删除,调用会报 `deprecated`。旧库里 `ambient`/`trial` 的群升级后
|
|
340
|
+
自动变为 `active`,其余为 `mention`。
|
|
341
|
+
- `inbox rooms` 回读里的 `tier`(由两轴推导)、`contextMode`(恒为 `off`)/`ambientMode` 只为兼容旧界面保留,
|
|
342
|
+
以 `capture`/`route` 为准;`legacySmart=true` 表示该群是旧 `smart` 档(@ 不过小模型)。
|
|
343
|
+
|
|
344
|
+
## 六、停止、卸载、删除与平台 Bot 下线
|
|
345
|
+
|
|
346
|
+
这四个动作不可混用:
|
|
347
|
+
|
|
348
|
+
1. `inbox disconnect <sourceId>`:只停止本机接收;durable Connector Profile、实例凭据、Session binding 和历史都保留,可 `resume`。
|
|
349
|
+
2. Session UI 中“卸载信源”:只删除 source→Session binding;Connector Profile 与监听仍存在。
|
|
350
|
+
3. `inbox remove <sourceId>`:永久删除**本地 Connector Profile**。裸调用只输出权威预检,不执行删除;必须阅读其中的平台、稳定 Bot external ID、监听状态、Session binding、凭据状态及 cloud ownership,再由用户明确确认后使用 `--confirm <同一sourceId>`。删除会停止 listener、解除 binding,并删除 profile/pause/dedup;Profile 中的凭据引用随之删除,但 vault 中的凭据值默认保留,Session 与聊天历史也保留。不要擅自删除 vault value;未来只有在用户独立明确选择且 Host 能证明凭据 ownership/sharing 时才允许清理。
|
|
351
|
+
4. 厂商 Bot 的下线、撤销发布或永久删除是独立的平台写操作,绝不是本地 Profile 删除的默认副作用。
|
|
352
|
+
|
|
353
|
+
硬门禁:
|
|
354
|
+
|
|
355
|
+
- 预检显示**新鲜确认过**的 `committed` 或 `recalling` cloud ownership 时禁止本地删除,必须先 recall 或完成权威 release;不得清掉本地母版绕过监听权。本地 ownership 只是带 TTL(10 分钟)的缓存:所有权记录已过期、或本机离线/未登录导致无法向控制面核实时不构成硬门禁(Host 会放行本地操作并记 warn),但云端所有权未确认释放前仍应优先 recall,不要制造双活。
|
|
356
|
+
- 预检之后若 Profile revision 或 Session binding 变化,compare-and-delete 会失败;重新预检,不得强行重试旧确认。
|
|
357
|
+
- 平台 Bot 清理只能在本地 Profile 删除完成后作为可选 Agent 动线发起。激活精确 Platform Skill,按稳定 external ID 重新只读 inventory,不得选 current/default/第一个 Bot,不得显示凭据;先解释平台支持的停止/撤销发布/永久删除及后果,再让用户二次确认具体动作。
|
|
358
|
+
- 平台清理失败不回滚或伪装本地 Profile;应独立报告,可稍后凭 stable external ID 重试。
|
|
359
|
+
|
|
360
|
+
## 七、Raw event、回复与完成判据
|
|
361
|
+
|
|
362
|
+
Connector 只把 vendor-native raw payload 投递给 Session;driver 可提取 dedup/route 字段做可靠性,但不做业务归一。Agent收到事件后激活模板的 `connectSkill`,按平台 Skill解释和回复。
|
|
363
|
+
|
|
364
|
+
除 dedup/route 外,driver 还可做 **ingress 准入过滤**:例如 telegram driver 支持通过实例 `config`(`inbox subscribe --config-json`)设置 `allowedChatIds` / `allowedUserIds` 白名单,未命中的事件在 Gateway 层直接丢弃、不进 Session、不触发回复。这是准入控制(放行/丢弃),不改写 payload 内容,因此不违反"不做业务归一"。具体白名单字段与取值方式见对应平台 Skill(Telegram 平台本身不提供访问控制,此白名单是唯一的按 Bot 限制来源的手段)。
|
|
365
|
+
|
|
366
|
+
任务完成必须验证:
|
|
367
|
+
|
|
368
|
+
1. CLI inventory 中选定的是明确 Bot external ID;
|
|
369
|
+
2. Connector instance 与该 external ID 一一对应,没有依赖 CLI current profile;
|
|
370
|
+
3. secret 未进入 transcript/argv/log;
|
|
371
|
+
4. `inbox status` 稳定为 `receiving`;
|
|
372
|
+
5. 真实 DM/@ 到达目标 Session;
|
|
373
|
+
6. 回复使用正确 Bot 实例;
|
|
374
|
+
7. CLI 切换 current profile 或被移除后,已导入 Connector 重启仍能 receiving。
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: "inbox_focus"
|
|
3
|
+
name: "Connector Focus"
|
|
4
|
+
description: "Read or replace what the owner of THIS conversation cares about, used to judge every connector message (IM groups, mail, ...) mounted on it: wake you, keep as context, or ignore. Call it whenever the user says what they want to be told about or bothered with from now on, that something no longer matters, or that priorities changed (e.g. '以后订饭的消息也叫我', '发票的事别再提醒我'). Always get first, merge the change into ONE complete paragraph, then set it (set replaces the whole text). Afterwards tell the user the current focus in one sentence."
|
|
5
|
+
category: "system"
|
|
6
|
+
version: "1.0.0"
|
|
7
|
+
supports_background: false
|
|
8
|
+
# Fully externalized plugin tool: declaration (this file) and execution body
|
|
9
|
+
# (the bundled `cteno-inbox` CLI) both ship in the cteno.inbox plugin. State
|
|
10
|
+
# lives in $CTENO_PLUGIN_DATA_DIR/focus.json, keyed by $CTENO_SESSION_ID.
|
|
11
|
+
# Contract: exit code decides success; stdout carries the agent-facing result.
|
|
12
|
+
handler:
|
|
13
|
+
type: command
|
|
14
|
+
command: cteno-inbox
|
|
15
|
+
args: ["tool"]
|
|
16
|
+
timeout_secs: 10
|
|
17
|
+
input_schema:
|
|
18
|
+
type: object
|
|
19
|
+
properties:
|
|
20
|
+
action:
|
|
21
|
+
type: string
|
|
22
|
+
enum: ["get", "set", "clear"]
|
|
23
|
+
description: "get = current focus (default); set = replace it with `focus`; clear = remove it (back to the generic default)."
|
|
24
|
+
focus:
|
|
25
|
+
type: string
|
|
26
|
+
description: "For set: the whole new focus as a few plain sentences (≤1000 characters), already merged with what was there before."
|
|
27
|
+
history:
|
|
28
|
+
type: boolean
|
|
29
|
+
description: "For get: also return earlier versions (newest first)."
|
|
30
|
+
required: ["action"]
|
|
31
|
+
additionalProperties: false
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
# inbox_focus
|
|
35
|
+
|
|
36
|
+
The focus belongs to the current conversation: every connector mounted on it is
|
|
37
|
+
judged against the same text, and an edit applies to the very next message.
|
|
38
|
+
A room's own `--focus` (set with `cteno-host-cli inbox room-policy`) adds to it
|
|
39
|
+
for that group only.
|
|
40
|
+
|
|
41
|
+
- `{"action":"get"}` → `{focus, isSet, updatedAt}`
|
|
42
|
+
- `{"action":"get","history":true}` → also `history: [{focus, updatedAt}]`
|
|
43
|
+
- `{"action":"set","focus":"<whole paragraph>"}` → `{changed, focus, …}`; unchanged text is a no-op
|
|
44
|
+
- `{"action":"clear"}`
|