@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.
Files changed (116) hide show
  1. package/LICENSE.daemon +31 -0
  2. package/README.md +4 -2
  3. package/agents/flow-analyzer/AGENT.md +71 -0
  4. package/bin/cteno-host-cli +4 -0
  5. package/bin/cteno-wiki +0 -0
  6. package/bin/rg +0 -0
  7. package/bin/secret-exec +0 -0
  8. package/manifest.json +12 -0
  9. package/package.json +33 -4
  10. package/plugins/agent-secrets/.cteno-plugin/plugin.json +133 -0
  11. package/plugins/agent-secrets/bin/linux-x64/fnox +4 -0
  12. package/plugins/agent-secrets/bin/linux-x64/secret-exec +0 -0
  13. package/plugins/agent-secrets/skills/secret/SKILL.md +201 -0
  14. package/plugins/cteno.browser/.cteno-plugin/plugin.json +65 -0
  15. package/plugins/cteno.browser/skills/browser/SKILL.md +28 -0
  16. package/plugins/cteno.inbox/.cteno-plugin/plugin.json +132 -0
  17. package/plugins/cteno.inbox/bin/all/cteno-connector-credential +31 -0
  18. package/plugins/cteno.inbox/bin/all/cteno-telegram-connector +33 -0
  19. package/plugins/cteno.inbox/bin/linux-x64/cteno-inbox +0 -0
  20. package/plugins/cteno.inbox/skills/cteno-inbox/SKILL.md +374 -0
  21. package/plugins/cteno.inbox/tools/inbox_focus/TOOL.md +44 -0
  22. package/plugins/cteno.memory/.cteno-plugin/plugin.json +176 -0
  23. package/plugins/cteno.memory/agents/llm-wiki/AGENT.md +96 -0
  24. package/plugins/cteno.memory/bin/linux-x64/cteno-wiki +0 -0
  25. package/plugins/cteno.memory/skills/codebase-wiki/SKILL.md +52 -0
  26. package/plugins/cteno.memory/skills/cteno.memory.native/SKILL.md +110 -0
  27. package/plugins/cteno.memory/tools/memory/TOOL.md +492 -0
  28. package/plugins/cteno.memory/ui/index.html +822 -0
  29. package/plugins/cteno.models/.cteno-plugin/plugin.json +191 -0
  30. package/plugins/cteno.websearch/.cteno-plugin/plugin.json +45 -0
  31. package/plugins/jev/.cteno-plugin/plugin.json +309 -0
  32. package/plugins/jev/skills/jev-browser/SKILL.md +66 -0
  33. package/plugins/jev/skills/jev-browser/jev_browser.py +335 -0
  34. package/skills/agent-create/SKILL.md +86 -0
  35. package/skills/cteno-host-cli/SKILL.md +256 -0
  36. package/skills/docx/LICENSE +23 -0
  37. package/skills/docx/NOTICE.md +8 -0
  38. package/skills/docx/SKILL.md +69 -0
  39. package/skills/docx/references/cjk_typography.md +357 -0
  40. package/skills/docx/references/design_principles.md +819 -0
  41. package/skills/docx/references/revisions-and-comments.md +88 -0
  42. package/skills/docx/references/track_changes_guide.md +200 -0
  43. package/skills/docx/references/typography_guide.md +294 -0
  44. package/skills/docx/scripts/cjk_fonts.py +244 -0
  45. package/skills/docx/scripts/docx_comments.py +289 -0
  46. package/skills/docx/scripts/docx_common.py +92 -0
  47. package/skills/docx/scripts/docx_create.py +259 -0
  48. package/skills/docx/scripts/docx_edit.py +250 -0
  49. package/skills/docx/scripts/docx_read.py +149 -0
  50. package/skills/docx/scripts/docx_revisions.py +146 -0
  51. package/skills/docx/scripts/docx_template.py +70 -0
  52. package/skills/docx/scripts/docx_validate.py +156 -0
  53. package/skills/docx/scripts/render.py +250 -0
  54. package/skills/docx/tests/test_docx_skill.py +525 -0
  55. package/skills/home-devices/SKILL.md +121 -0
  56. package/skills/mcp/SKILL.md +72 -0
  57. package/skills/mcp/scripts/mcp_setup.py +439 -0
  58. package/skills/pdf/LICENSE +23 -0
  59. package/skills/pdf/NOTICE.md +8 -0
  60. package/skills/pdf/SKILL.md +63 -0
  61. package/skills/pdf/references/design-tokens.md +381 -0
  62. package/skills/pdf/references/forms.md +99 -0
  63. package/skills/pdf/references/nano-pdf-editing.md +40 -0
  64. package/skills/pdf/references/ocr-extraction.md +165 -0
  65. package/skills/pdf/scripts/_raster.py +76 -0
  66. package/skills/pdf/scripts/extract_marker.py +88 -0
  67. package/skills/pdf/scripts/extract_pymupdf.py +101 -0
  68. package/skills/pdf/scripts/pdf_create.py +191 -0
  69. package/skills/pdf/scripts/pdf_fill_form.py +97 -0
  70. package/skills/pdf/scripts/pdf_form_layout.py +168 -0
  71. package/skills/pdf/scripts/pdf_make_form.py +145 -0
  72. package/skills/pdf/scripts/pdf_merge.py +50 -0
  73. package/skills/pdf/scripts/pdf_meta.py +115 -0
  74. package/skills/pdf/scripts/pdf_page_image.py +99 -0
  75. package/skills/pdf/scripts/pdf_read.py +153 -0
  76. package/skills/pdf/scripts/pdf_secure.py +71 -0
  77. package/skills/pdf/scripts/pdf_split.py +84 -0
  78. package/skills/pdf/scripts/pdf_stamp.py +143 -0
  79. package/skills/pdf/scripts/pdf_watermark.py +51 -0
  80. package/skills/pdf/scripts/render.py +250 -0
  81. package/skills/pdf/tests/test_pdf_skill.py +415 -0
  82. package/skills/pptx/LICENSE +23 -0
  83. package/skills/pptx/NOTICE.md +8 -0
  84. package/skills/pptx/SKILL.md +62 -0
  85. package/skills/pptx/references/design-system.md +392 -0
  86. package/skills/pptx/references/pptxgenjs.md +420 -0
  87. package/skills/pptx/references/slide-types.md +413 -0
  88. package/skills/pptx/scripts/cjk_fonts.py +244 -0
  89. package/skills/pptx/scripts/pptx_create.py +240 -0
  90. package/skills/pptx/scripts/pptx_edit.py +436 -0
  91. package/skills/pptx/scripts/pptx_from_template.py +88 -0
  92. package/skills/pptx/scripts/pptx_read.py +131 -0
  93. package/skills/pptx/scripts/pptx_render.py +96 -0
  94. package/skills/pptx/scripts/render.py +250 -0
  95. package/skills/pptx/tests/test_powerpoint_skill.py +477 -0
  96. package/skills/skill-create/SKILL.md +114 -0
  97. package/skills/video-generation/SKILL.md +27 -0
  98. package/skills/xlsx/LICENSE +23 -0
  99. package/skills/xlsx/NOTICE.md +8 -0
  100. package/skills/xlsx/SKILL.md +63 -0
  101. package/skills/xlsx/references/format.md +71 -0
  102. package/skills/xlsx/references/restructuring.md +71 -0
  103. package/skills/xlsx/scripts/csv_to_xlsx.py +110 -0
  104. package/skills/xlsx/scripts/formula_check.py +428 -0
  105. package/skills/xlsx/scripts/render.py +250 -0
  106. package/skills/xlsx/scripts/xlsx_create.py +265 -0
  107. package/skills/xlsx/scripts/xlsx_edit.py +269 -0
  108. package/skills/xlsx/scripts/xlsx_read.py +166 -0
  109. package/skills/xlsx/scripts/xlsx_recalc.py +119 -0
  110. package/skills/xlsx/scripts/xlsx_restructure.py +343 -0
  111. package/skills/xlsx/scripts/xlsx_to_csv.py +75 -0
  112. package/skills/xlsx/tests/test_xlsx_skill.py +544 -0
  113. package/tools/create_goal/TOOL.md +36 -0
  114. package/tools/image_generation/TOOL.md +81 -0
  115. package/tools/skill/TOOL.md +117 -0
  116. package/tools/update_profile_notes/TOOL.md +39 -0
@@ -0,0 +1,256 @@
1
+ ---
2
+ id: cteno-host-cli
3
+ name: cteno-host-cli
4
+ description: "通过 cteno-host-cli 调用 CtenoHost 跨 session 能力,包括派出/跟进 thread、读取结果、管理 Agent、MCP、插件和远程机器"
5
+ when_to_use: "用户或 Agent 需要通过公共 CLI 操作跨 session Host 能力时使用"
6
+ version: "2.0.0"
7
+ tags:
8
+ - cteno
9
+ - host
10
+ - cli
11
+ - session
12
+ user_invocable: true
13
+ disable_model_invocation: false
14
+ metadata:
15
+ display-name: "Cteno Host CLI"
16
+ ---
17
+
18
+ # Cteno Host CLI
19
+
20
+ > Bot/Session迁移功能暂时停用。不得执行session export/import、inbox迁移/export/import/clear-ownership/delete-duty,或经远程RPC绕过;保留既有迁移数据,不恢复、不清锁。
21
+
22
+ `cteno-host-cli` 是跨 session 的公共入口。**参数与子命令以当前二进制 help 为准,不在 Skill 复制清单**:
23
+
24
+ ```bash
25
+ cteno-host-cli help
26
+ cteno-host-cli <domain> --help
27
+ cteno-host-cli <domain> <subcommand> --help
28
+ ```
29
+
30
+ 不确定命令是否存在或参数如何写时,先逐层查看 help;不要凭旧记忆猜私有 RPC。
31
+
32
+ ## 必须理解的语义
33
+
34
+ - **登录热生效**:`auth login` 只在目标本机执行,不接受 `--machine`。运行中 daemon 须支持 `auth-live-login-commit-v1`,CLI 经本地 owner IPC 提交到 daemon 的 canonical AuthStore,不复制认证文件、不重启登录流程。无 daemon 时只保存离线凭据;`Login approved`/认证为 yes 不等于 relay 已上线,核对机器实际连接。旧 daemon 缺能力或提交失败必须明确报错,不静默回退文件写入冒称生效。
35
+
36
+ - **跨机密钥供应**:双方本机 `plugin secure-supply --help`;owner 控制拒绝 `--machine`,目标要求 `machine-secure-supply-target-v1`,源要求 `machine-secure-supply-source-v1`。源 source-prepare 绑定 vault 引用/目标/插件/set/条目/TTL,目标 prepare 绑定同一摘要与源公钥;核对双方指纹及全部 binding,以及目标 proposal 的 pluginGeneration(不可变插件包内容指纹,已绑定目标 digest);用户明确批准目标 digest 后,源 send 还须明确批准 sourceDigest、目标完整 proposal 与 targetFingerprint。禁止自动 prepare→confirm/send,研发授权不是密钥供应授权;CLI 的 --confirm 只是执行参数,不是用户批准证据,Agent 执行 confirm/send 前必须走宿主批准门禁并绑定精确审查快照。明文只在 daemon resolve→box→目标stdin;一次操作先消费再写、重启失效,pluginGeneration 包含动作 owner 及其声明的执行依赖,任一升级/禁用后旧审批拒绝;错误不自动重试,旧host不降级。
37
+ - **媒体配置**:图片、识图与视频后端独立于聊天 profile;设置统一从 `profile --help` 的公共 CLI 契约进入 daemon,不直接编辑配置文件或使用 GUI 专有逻辑。视频 BYOK 声明须显式协议、模型、HTTPS 根地址、vault 引用及生成规格;协议不从模型名推断。清除视频默认只表示未选择,不自动替换模型;删除当前默认前先显式换选或清除。托管目录由服务端声明,不通过 BYOK 命令伪造。生成验收仍须自然语言真 Agent session 与实际文件检查,CLI 参数解析通过不等于生成链路通过。
38
+
39
+ - **Public page 成果读取**:扩展页面须 gate `plugin-public-page-results-v1`,按安装 manifest 审核完整请求集合。支持 `sessions.read.v1`、`sessions.events.v1`(durable poll)、`sessions.results.v1`;输出文本还需独立 `sessions.outputs.v1` 且同时批准 results。只读状态不隐含成果;拒绝写操作、未知/重复 capability 和 live subscribe。所有资源仍绑定单一精确 session,execution approval 检查不因 page approval 绕过。参考插件的 `status` 与 `task-results` 是独立页面批准,不自动扩大旧 grant。
40
+
41
+ - **Public page owner 控制**:先看 `public-page --help`。`open <plugin-id> <page-id> <existing-session-id>` 只创建未授权 document;`review <channel>` 返回当前 proposal/digest/documentEpoch。只有 owner 明确同意该快照后才可 `approve <channel> --digest <sha256> --document-epoch <epoch> --confirm`,严禁开发时自动 open→approve 或替真实用户授权。`revoke <channel>` / `close <channel>` 都走现有 public-page-close,失效后必须重新 open/review,不能迁移批准。仅本地 owner RPC client,拒绝 `--machine`;channel/digest 留在可信容器,bearer 永不出 daemon,页面只得到 public adapter 结果,绝不能把 CLI/owner RPC 暴露给页面。需 `plugin-public-page-owner-v2` capability;旧 host 不自动降级批准。GUI route `/public-plugin-page` 的真实握手、读取、导航和撤销须独立验收,源字符串断言不是 GUI 通过。
42
+
43
+ - **公共事件 catch-up**:`public.v1.sessions.events` 需单独批准 `sessions.events.v1` + exact `sessionRead`;send 不隐含读取或订阅。仅 durable poll,非实时 push;`limit` 1..1000,空页也保存 `nextCursor`,`hasMore` 可包含私有/未知事件。失效返回 `CURSOR_EXPIRED`,显式从头并按 eventId 去重;详见 `docs/testing/public-events-2026-09-12.md`。
44
+
45
+ - **第三方回答文本**:results 请求 `includeOutput:true` 还须显式批准 `sessions.outputs.v1`;`sessions.results.v1`/精确 `sessionRead` 仍必需,send 不隐含读取。只返回 runtime turn 明确链接的最终 assistant Message.id 文本,历史无关联/失败/输出尚未落账均不返回 output,不猜最新 assistant。文本每条最多 64KiB、每页最多16条;超限拒绝不截断。见 `docs/testing/public-outputs-2026-09-12.md`。
46
+
47
+ - **第三方结果读取**:`public.v1.sessions.results` / `sessions.results.v1` 在现有 messages 单注册上提供 canonical Message.id→TurnRecord 终态及分页身份历史,不透传 owner transcript、工具、profile 或错误详情。须单独经现有 prepare/confirm 显式批准精确 `sessionRead`(含 `public.describe.v1` 与 `sessions.results.v1`),send 绝不隐含 read;仅 execution-approved session 可读。`noTerminalRecord`/accepted/idle 不等于成功;`failed` 保持失败。CLI 的 owner `session messages/trace` 不是 public bearer adapter,不可拿 owner 输出绕过授权。SDK 根/游标及后续 events/create 边界见 `docs/testing/public-results-2026-09-12.md`。
48
+
49
+ - **执行批准审查**:`public-authority execution-prepare --proposal <JSON-file>` 校验现有 session、明确 canonical profile、绝对规范化 workdir、精确 tools 数组和 exact sessionSend grant/expiry。仅 owner 明确同意完整摘要后才 `execution-confirm`;独占 reservation 排除 Owner/Public/其他确认,复验 agent-kind/profile/workdir/birth,私有 vault 交付后原子提交 grant+approval+audit+原 birth 绑定,再发布 ledger activation。不继承 owner/bypass prefs;提交前失败无可用 grant,提交后 activation 失败则保留受限审批、禁止执行,不能宣称未授权或自动重试。prepare 的 executionEnabled 读取真实 registry 安装状态,不等于授予执行权限。
50
+ - **Activation 恢复**:仅本地 owner `activation-review <session-id> --review-before <Unix-seconds>`,期限必须在未来五分钟内。核对原审批、grant revision、birth、profile/workdir/tools 与摘要,再 `activation-confirm <session-id> --review-before <同一期限> --digest <sha256> --confirm`。要求 `public-activation-recovery-owner-v1`;不发新凭据或新 grant、不启动模型。缺原始 birth 证据、已撤销/过期或 profile/birth 变化则拒绝;禁止直接改 DB/ledger 补造恢复。完整真实故障恢复资格仍须另验。
51
+ - **Relay 授权显式区分**:`public-authority prepare` 保持 Local;只有 `prepare-relay --proposal <JSON-file>` 选择 Relay,proposal 的 `ownerBindingId` 必须是目标账号 ID,不接受 `local-owner`。transport/host 纳入 digest 确认;grant 不等于 route ticket,不自动换发或升级。加密另需本地 owner `pair-prepare --proposal <JSON-file>`(ownerId/clientInstanceId/clientPublicKey/expiresAt Unix 毫秒,最长5分钟),带外核对双方 SHA-256 公钥指纹及完整 pairing 后才可 `pair-confirm --pair-id <id> --digest <digest> --confirm`;`pair-revoke <id> --confirm` 撤销加密配对。绝不自动确认或信任 server 转发公钥。私钥只进 vault、缺 pairing capability OFF、重启必须重新批准。wire/独立测试向量见 `docs/architecture/public-plugin-relay-box-v1.md`;SDK relay 与加密订阅已接线,可用性仍以实际认证 discovery、pair/ticket/grant 和 host readiness 为准,验收范围见 `docs/public-plugin-developer-guide.md`,不得发送明文 bearer 或复用 owner DataKey。
52
+ - **第三方授权**:`public-authority --help` 是 owner 控制入口,不是 public dispatcher。先 prepare 并向 owner 展示完整 proposal、host/transport、digest、expiry;只有 owner 明确同意该快照后才能 confirm。开发实施授权绝不是代第三方同意权限;禁止自动 prepare→confirm、默认期限或全权限。凭据只由 daemon 经现有 vault hook 保存,CLI 仅返回 `secret://` 引用;缺失/锁定 vault 必须失败,不得退回打印、argv、环境变量或临时明文文件。prepare 支持已安装的 discovery/read/results/outputs/events/subscribe;send/create 使用独立执行审批。资源与 capability 均须精确匹配,不由发现结果推定权限。CLI 不支持 `--machine`;撤销后用 status 复核。
53
+ - **执行撤销**:`public-authority revoke <grant-id> --confirm --abort-bound-runs` 可在提交撤销后请求已有 HostSessionService 协作 abort,仅命中可信 ingress 明确绑定的 grant-owned 运行,不遍历可读 session。普通 revoke 只拒绝后续准入;两者均不回滚已发生副作用,也不保证外部进程立刻停止。执行批准经独立 owner execution/create 审批接入 public send/create;不默认批准 profile/workdir/tools。
54
+ - **Create 批准审查**:`public-authority create-prepare --proposal <JSON-file>` 必填 workspaceId/profileId/canonical workdir/clientInstanceId/expiresAt/tools/sessionActions;UUID 与 policyId 由 daemon 生成。省略 transport 或 local 保持 local-owner;远端 create 必须显式 transport:relay 与匹配 account/route 的 ownerBindingId,摘要绑定二者;不代替独立 pair/ticket。核对完整摘要后 owner 才能 `create-confirm --proposal-id <id> --digest <sha256> --expires-at <Unix-seconds> --confirm`。动作逐项选择 sessionSend/sessionRead/resultRead/eventSubscribe;[] 仅 create,events 指 durable polling,不授 live 或 output 文本。私有交付前后复验并原子提交;现有 create-thread 单注册项承载 public adapter,是否可用以实际 createAdvertised/认证 discovery 为准,确认不是出生成功。开发者不得替 owner 实际授权。
55
+ - **Workspace 登记**:`public-authority workspace-register --persona-id <existing-agent-persona-id> --workdir <canonical-absolute-directory> --confirm` 仅本地 owner IPC,要求 `public-workspace-owner-v1`。personaId 不是推理 profileId;只创建新绑定,重复或覆盖拒绝。返回 workspaceId 后仍需独立 create approval,登记不授予 public 权限。
56
+ - **一次性任务**:`run` 会等待初始 turn 完成,默认再读取 normalized trace;最终 JSON 只有 `sessionId/success`,模型回答在 trace 中。行为验收以 `session trace` 为准。
57
+ - **可继续对话**:`session/thread create` 后用 `session/thread chat`。`chat` 默认等待整轮,并如实返回本轮结果:daemon 有 `send.wait.turn-result.v1` 时,模型 404/401/429 重试耗尽/回合超时会回 `success:false` + `errorKind` + 原始错误(含请求 URL),CLI 以非零码退出 `turn failed [<kind>]: <error>`;`--no-wait` 只表示投递完成,之后仍要读状态/trace。
58
+ - **报告身份验收**:`session chat --handoff-id <Message.id> -m <原始报告>` 走报告通道,忙时注入当前轮、空闲时唤醒。仅用于已授权报告投递/重放;重试复用同 ID,同文不同 ID 是两条报告。响应 `acknowledged:true` 仅代表父 canonical ledger 已接受,不代表 LLM 完成。`success:true, acknowledged:false` 仍须以同 ID 重试确认;不要改文本后复用旧 ID。
59
+ - **中途补话**:普通 `chat --no-wait` 不等同于强制 steering。要把已排队消息立即注入 live turn,使用 queue 的 steer/dispatch 操作;普通 queue 消息默认 hold-until-idle,并在之后独立成轮。具体动作先看 `session queue --help`。
60
+ - **结果与完成通知**:普通顶层 thread 不会像 runtime-owned subagent/DAG 一样自动向派出方回灌 `[Task Complete]`。调用方应保留 thread id,并读取 `session get/trace`。后台工具 run 的 completion 只回到其 owning session。
61
+ - **后台子 run**:`run --parent-session <id>` 让这次 run 成为该 session 的后台子会话(账本记血缘,不出现在历史列表,只在后台任务里可见);子会话结束却没有最终回答时,会以一条 `[Task Failed] <label> <child>` 回灌给父会话(首行之后是一句人话 + 一个动作,原始错误在 `<internal-details>` 里,转述给用户时只说那句人话),且不自动重跑。`--timeout-secs <s>`(默认 3600,≥10)是 CLI 等待的墙钟上限,到点返回 `status: "still_running"`(「还在做,过会儿再看」)——**超时不是失败**,子会话继续跑,做完照常回灌;`--max-turns` 只限轮数,与等待时长无关。`--no-handback` 只隐藏、不回灌。子会话在 turn 结束后才被判定失败时(例如 dream 自报失败),用 `run hand-back <child> --error <text> [--label <l>]` 回灌一次,同一个子会话幂等。
62
+ - **远端委派关闭**:`cteno-host-cli --machine <id> session close-delegated <child> --parent <原始父会话ID>`,要求目标 `remote-task-close-v1` 与认证 transport 调用身份;parent 参数不授予权限,目标复核子账本血缘与终态,拒绝运行中、等待决策、非委派及外机归属。旧目标/失败无普通 close 回退。并发关闭验收尚未完成,不以此入口代替最终行为验收。
63
+ - **生命周期**:`session close` / archive 只释放 live executor、改变列表投影,canonical SessionLedger 仍完整,可再次 `chat`/resume/reopen。只有显式 `session delete` 是永久 purge;它会清 ledger、runtime facts、pending、links 与 artifacts,不能用 close 代替删除,也不能把 archived 当不存在。
64
+ - **未读徽标**:`unread` 是 daemon 持有的布尔(回合以 assistant 回复结束即置真,含 autonomous 回合),`session get` 可见 `unread`/`lastReplyTurnId`。`session mark-read <id> [--seen-turn <turn_id>]` 只在 turn 为最新回复时清除(防跨端迟到清除);`session mark-unread <id>` 置真。
65
+ - **压缩/分支**:`session compact` 追加 coverage/digest checkpoint,不复制 recent tail;`session rewind` 与 fork 通过 ledger ancestry/head 实现,不物理改写旧消息。命令失败时以 fail-closed 错误处理,禁止从旧 context snapshot 猜恢复状态。
66
+ - **同请求提醒**:capabilities 的 methods 包含 `card-remind` 时,`session card remind <card_id>` 仅提醒现有 pending 请求,复用原 ID/操作页链接;不发新卡、不重置状态、不产生回答或执行授权。终态和其他机器请求拒绝提醒。
67
+ - **请求定位链接**:先查 `session card capabilities` 的 methods 包含 `card-link`,再 `session card link <card_id>`。返回同一 canonical request/machine 的 `/interact?...` 第一方相对 path(终态仍可回看);只定位,不携授权、token或secret,不凭链接执行操作。不要硬编码域名或拼外站认证链接。
68
+ - **权限**:无人值守调用必须显式选择合适的 permission mode;默认模式可能等待无人处理的权限卡。不要无条件使用 bypass,按用户授权选择。
69
+ - **托管 worktree 是 daemon 的,不是你的**:`dispatch_task` 的 `worktree: "<短名>"` 由 daemon 在 `<主检出>-wt-<短名>` 建、在节点终态时**自动回收**(分支 `cteno-wt/<短名>` 与其上的提交保留,只删检出目录)。所以**不要**为派活手搓 `git worktree add`,也不要在 kickoff 里写「干完记得删」——拆除责任必须落在比节点活得久的组件上。想看 daemon 当前托管着哪些树,用 `cteno-host-cli storage worktrees`(只读;`session worktrees` 是另一回事,它列的是某个 thread workdir 下的 git worktree)。
70
+ - **交回要附证据、合并要过复审**:`report_to_owner(status=done)` 带 `evidence:[{command,result,files}]`;runtime 拿命令去核子线程自己账本里的 shell 调用,无证据的交回在派活方那边第一行显示「[警告:无证据交回]」。仓库 main 上有 `REVIEW.md` 时,合并只能走 `merge_review`(op=review/status/close/merge;shell 里直接往 main `git merge` 会被拒):全新上下文的只读 `merge-reviewer` 按 Important/Nit/Pre-existing 出意见,Important 必须修掉或 op=close 写理由;第 4 轮起只审新增 diff。本仓库的 `REVIEW.md` 未声明 `high_risk`,没有「命中高风险路径需人工审」这道拦截(声明了 `high_risk` 的仓库才有)。CLI 走 `cteno-host-cli session invoke-tool <sid> merge_review --input '{"op":"status","repo":"<wt>"}'`;复审记录在 `<git common dir>/cteno-review/<branch>.json`。见 `docs/architecture/review-gates.md`。
71
+ - **远程机器**:先 `machines list`(账号级列表,本身不接受 `--machine`),再把顶层 `--machine <id|name>` 放在业务命令之前。它只切换 transport;auth/host/status/connect/webview 等本地命令不能远程执行。长任务注意远程 RPC deadline,异步派出后再查状态通常更稳妥。
72
+ - **跨机派发前先预检,别凭记忆写 machine id 和路径**。跨机是盲派:你看不见那台机器的磁盘,也不知道它装了哪些模型。两个**只读、不建 session**的命令回答这件事:
73
+ - `cteno-host-cli --machine <id|name> machines describe` —— 平台/arch、daemon 版本、capability,以及**它真正能跑的 model profile 列表**。`dispatch_task` 带 `machine` + `profile_id` 前必须看这个:对端没有的 profile 根本无法推理。
74
+ - `cteno-host-cli --machine <id|name> machines probe-path --path <dir>` —— 该目录是否存在;若是 git 检出,返回 remote url、分支、HEAD sha、是否 dirty。判据不是「有没有这个目录」,而是「**这是不是我要的那个仓库那个分支**」——同名不同仓是最危险的一种「成功」,它不报错,只是对着错误的代码干活。
75
+
76
+ `dispatch_task` 带 `machine` 时,daemon 会在**建图之前**就串行走三道闸(capability 握手 → profile 存在 → 目录可用),任一不过就把可读错误**同步作为 dispatch_task 的返回值**丢回来,并附上对端实际值。此时**没有建图、也没有在对端建任何 session**,你直接改参数重派即可。所以漏检的代价是一条错误信息而不是孤儿会话;但先查仍然更省事:一次拿到正确答案,而不是靠闸门反复拒绝来试。
77
+
78
+ ## 回答 agent 的决策选项题(decision mode choice)
79
+
80
+ `session question list [<session_id>]` 列出挂起的选项题(含 `recommended`);旧 `ask_user` 调用仍可续办。`session question answer <question_id> --choice <label> [--other <text>] [--note <批注>]`(多子题加 `--id <qid>`,或 `--answers '<json>'`)代用户作答;`session question dismiss <question_id> [--reason <text>]` 撤销(agent 得到 `dismissed`,不假定任何选项);加 `--reply <text>` 则记为 `replied`,文字即用户自己的回答。无人作答和「稍后」都不会自动采用推荐项,更不构成操作批准。
81
+
82
+ ## 拍板卡片(card:权限 / 选项题 / 密钥 / 确认)
83
+
84
+ 权限、`ask_user`、`request_secret`、`confirm_action` 都投影成 Card,带 `sessionId`(来源线程)、`jellyId`(所属主对话)、`kind`(`permission|question|secret|confirm|suggestion`,未知值要容忍)、`blocking`。线程只经工具闸门出卡;线程的选项题经 `ask_owner` 冒泡到主对话。先 `session card capabilities` 确认 `features` 含 `cards.later`。
85
+
86
+ - `session card list [--jelly <主对话 session_id>] [--include-later | --later] [--status <…|any>]`:默认只列当前 pending、不含「稍后」;`--later` 只列稍后(手动搁置 + 超时过期)。
87
+ - `session card answer <card_id> --choice <id>`:按 kind 分派回原 waiter。权限 `allow/deny`;选项题 `--choice <label>`(多子题用 `qid=label`,`dismiss` 撤销);确认卡 `confirm|reject`(`confirm_action` 类别含 `device`;`risk: high` 的卡永远不会被规则自动放行);密钥卡只能 `decline`,**值只走 secret-prompt 加密通道**,永不经 card/消息。
88
+ - `session card snooze <card_id> --later`:搁进「稍后」(仍 pending,waiter 照常计时)。
89
+ - 等待超时(默认 300s,`CTENO_PERMISSION_TIMEOUT_SECS` 可调)仍自动拒绝,卡变 `expired`:没碰过的卡直接消失、不计数;只有手动点过「稍后」的卡留在「稍后」,`session card refresh <card_id>` 把说明消息发回来源线程让其重新发起这一步。
90
+ - `session card flag`(`station-flag`,`features` 含 `station.flag`):这台机器上各 Jelly 角标之和 `{count,tone,perJelly}`,与头像角标同源,只读。
91
+ - 卡生命周期(`docs/architecture/decision-card-lifecycle.md`):会话归档/删除、`decision` 带 `supersedes:<旧 cardId>` 重新发卡、daemon 启动发现来源会话已结束——这三种情况旧卡变 `invalidated`(`--status invalidated` 可查,原因在 ledger);系统从不按时间清理 pending 卡。
92
+ - 插件(`features` 含 `attention.pluginSubscribe`):manifest 声明 `surfaces.attention.subscribe: true` 后,经 plugin-rpc `platform.attention.list` 读卡的最小投影,并收到 `attention.cardUpdated`;插件不能答卡。
93
+
94
+ ## 远程保险箱的配对设备(devices 域)
95
+
96
+ 手机远程解锁/初始化保险箱前,必须在**这台电脑本机**批准配对(relay 上调用会被拒,`--machine` 也不行):
97
+ - `cteno-host-cli devices pending` 看待批准请求(不含配对码);`devices approve <pairId> <手机上显示的6位码>` / `devices reject <pairId>`。码只能由用户从手机读给你或自己输入,不要猜;错一次该配对作废。
98
+ - `devices list` 列已配对设备;`devices revoke <deviceId>` 立即撤销。批准/撤销属于授权变更,按确认门禁先问用户。
99
+
100
+ ## 桌面驾驶权(desktop 域)
101
+
102
+ 多个 agent 共用一块屏幕时由 daemon 仲裁驾驶权(`docs/architecture/desktop-control-runtime-state.md`):真实指针/键盘的动作占整机,后台投递到窗口的动作只占那个应用;被占时 `computer_use` 返回 `DESKTOP_BUSY`(点名持有者与起始时间)或 `DESKTOP_STOPPED`(急停冷却中),不要硬抢。
103
+ - `cteno-host-cli desktop [state]`(RPC `desktop-claim-state`):当前持有者 `holders[{sessionId,label,kind:machine|app,app,sinceMs,pid}]`(含同机其他进程)与急停冷却 `cooldownUntilMs`,只读。
104
+ - `cteno-host-cli desktop estop`(RPC `desktop-claim-estop`):急停——释放所有 claim、抬起按键,并让所有进程 30 秒内拒绝驾驶。这是代用户按下停止键,只在用户要求或明显失控时用。
105
+
106
+ ## 历史证据与压缩后取回
107
+
108
+ 跨 session 验收先用 `session trace --help` 和 `session messages --help` 确认公共读接口。`trace` 用于检查模型实际执行的调用与结果;仅在最终回答中出现目标文字不算取回成功,尤其不能用用户测试提示中的文字作命中证据。`trace --full` 只关闭 CLI 行截断,不恢复宿主已截断的预览;读取原始证据用 `session messages <session_id> --message-id <id>`(可重复,最多 500 个),复用公共 `get-session-messages` 的精确 ID 读取,不直读 daemon SQLite。
109
+
110
+ **全局搜聊天记录**(daemon capability `session-search.v1`):`session search "<关键词>" [--limit <n>] [--active-only]` 跨对话搜用户说的话、Agent 回复和连接器收到的原始事件(`<external-source-event>` 的 payload),默认含已归档对话;工具输出、thinking、运行时通知和投递说明不参与搜索。每个结果给 `sessionId` 与命中的 `messageId`,要看完整原文用 `session messages <session_id> --message-id <messageId>`。连接器没挂到任何对话时消息会被拒收、不进任何聊天记录,搜不到;用 `inbox status` 的 `unboundRejectedEvents` / `lastUnboundRejectAt` 查看被拒收条数。
111
+
112
+ **增量跟读**(daemon capability `transcript-cursor.v1`):`session messages` 的每页带 `rev`(该会话 ledger 的 transcript revision)。轮询一个会话的新消息时用 `session messages <session_id> --after-seq <rev>`,只返回 `rev` 之后追加的可见消息和新的 `rev`;`reset: true`(rewind / 新增超过 `--limit` / 未知 rev)表示游标不可续,改回不带 `--after-seq` 的整页读取再续。`--after-seq` 不能与 `--message-id` 同用;不带它时输出与以前完全一致。见 `docs/architecture/transcript-sync-cursor.md`。
113
+
114
+ 当前 session 内让 Agent 使用 `session_history`:先 `list_windows` 定位压缩分段,再用 `list_items`(无查询词)或 `search_contents`(字面量查询)定位证据,随后 `read_call` 校验配对、`read_item` 按 Unicode scalar 分页。可组合 `kind/tool_name/is_error/window_id/start/end`;`start/end` 是源消息绝对索引,不是筛选后页码。分页保留 `snapshot_last_message_id`,边界失效就重新搜索。
115
+
116
+ 保留返回的 `message_id/entry_id/block_index`,不要只引用预览。精确工具条目的 `read_item` 会返回 `call_id`,可直接传给 `read_call`;整条消息含多个工具事件且未指定 `block_index` 时该字段为 null,须先通过归一化检索定位具体 block,不从记忆猜调用 ID。`source_completeness=stored_tool_text` 仅证明保存的工具返回文本可校验恢复,不证明工具读过的外部文件完整;`truncated/unavailable/unknown` 均不得改称完整。`has_more` 只说明本次分页。无需、也不应通过不存在的 `tool exec` 绕过运行时 session 身份注入。
117
+
118
+ ## 浏览器侧边栏身份与人机交接
119
+
120
+ 先读 `cteno-host-cli browser --help`。`targetId` 是引擎网页身份,`tabId` 是 daemon 侧边栏身份,不能互换;App tab 没有引擎 target。诊断列表用:
121
+
122
+ ```bash
123
+ cteno-host-cli browser --session-id <session-id> pane-state
124
+ ```
125
+
126
+ 普通可见 `open/new-tab` 应进入统一 tab 列表;hidden 不应出现。切换、交还后必须确认同一 `tabId` 仍在,不能只看命令 `success`。`assist` 保持 Agent 布局并恢复用户输入,`done` 恢复用户布局;均不创建替代 tab。跨网远程接手不再有浏览器远程围观(`browser-remote-viewer-offer` 已删除);需要人从手机/另一台电脑操作时用 `remote-control` 插件的 `cteno-remote grant` 签发 RustDesk 链接。
127
+
128
+ 页面链接 `target="_blank"` 创建的新网页由 daemon 主动发现并收养,不应等 Agent 再调用 `tabs`。验证时先只读 `pane-state` 并观察 GUI tab 栏;`tabs` 含兼容收养兜底,提前调用可能掩盖主动发现缺陷。GUI 连续验证脚本为 `scripts/e2e/pane-unified-probe.cjs <gui-cdp-port> <test-session-id>`,须初始化对应 worktree 的 dev 环境。
129
+
130
+ 用户关闭正在操作的 tab 时,Agent 应收到对应 session 的关闭消息并停止该页操作;不自动重开。Agent 自行关闭不能伪装成用户关闭。消息投递、实际消费和 GUI 可见性是不同验证点,不能用日志或 driver 字段代替真实验收。
131
+
132
+ `pane-state` 里 tab 的结局分两列:`failed[]` 只放真故障(打开/恢复失败),`suspended[]` 放引擎空闲退出、显式停止、空闲回收收起的 tab(可按 url 重开,不是错误)。引擎空闲退出后看到 `suspended` 是正常现象,不要当故障排查。清掉提示用 `cteno-host-cli browser --session-id <id> dismiss-ended [--tab-id <uuid>]`。见 `docs/architecture/browser-pane-tab-outcomes.md`。
133
+
134
+ 异常引擎换代与用户关闭不同:daemon 保留可见 tab 的身份并恢复页面,`pane-state` 中恢复期间为 `Opening`,可带 `recoverySourceTarget`(旧绑定,仅诊断数据,不是可操作 target);成功后保持 `tabId`,换成新 `kind.targetId` 并清除标记。不要把旧 targetId 自动替换或重试当作工具成功,需重新观察当前 tab。用户关闭的 `closedTabIds` 始终优先,不能通过恢复重开;恢复也不保证页面内未保存数据。
135
+
136
+ ## 帮用户添加模型(口头交办 → profile 落地)
137
+
138
+ 用户说「帮我把这个中转站加进来」「把 xx 分组的模型都加上」时,整件事在 `profile` 域里闭环,**不要让用户自己去设置页点**。
139
+
140
+ **1. 先拿 key,别在聊天里要。** 用户手上只有一把 key 和一个网址。API key 属于凭据,进了对话就进了 transcript、账本和模型上下文。调 `request_secret`(见 agent-secrets skill)让宿主在**用户那台设备**上弹安全输入框:
141
+
142
+ ```
143
+ request_secret(label: "中转站 API Key", purpose: "拉取模型目录并添加 profile", persist: true, name: "xxx-api-key")
144
+ → secret://vault/xxx-api-key
145
+ ```
146
+
147
+ 拿到引用后**原样内联**进命令。CLI 另有 `--api-key-env <ENV>`(从环境变量读,不进 argv);`--api-key <key>` 虽仍兼容但**不要用**——`ps` 和 shell history 都会看见。
148
+
149
+ **2. 先 discover 再 import,不要跳过。** `discover-models` 只读、不落库,用来把"这个端点到底有什么"摆给用户看。**别拉完就直接全导**:中转站动辄上百个模型,用户想要的通常是其中一小撮。
150
+
151
+ **3. 目录形状是声明的,不是猜的。** `--catalog-dialect`:
152
+ - 缺省 `openai` —— 标准 `/models` 平铺目录,绝大多数官方 provider。
153
+ - `new-api` —— one-api/new-api 系网关(聚合型中转站多是这一系)。它额外返回 `groups`:每组带中文说明、计费倍率 `ratio`、模型数,以及哪些是自动路由组。
154
+
155
+ 分不清就先按 `new-api` 试一次:**不对只会拉不到目录,不会写坏任何东西**。反过来用平铺方言拉聚合站,你会拿到一个几百项、没有分组的列表,那才是难办的。
156
+
157
+ **4. 按分组批量导入,并声明模型选择器分组。** 这里有两个不同维度,不能混为一谈:
158
+
159
+ - `--group <目录组名>`:从 new-api 目录中筛选**导入哪些模型**;只在发现/导入时使用。
160
+ - `--picker-group <显示名>`:保存到每个 Profile,决定导入后在**模型选择器与设置页显示在哪个独立分组**。
161
+
162
+ ```
163
+ cteno-host-cli profile import-models --base-url <url> --api-key-env KEY \
164
+ --catalog-dialect new-api --group "<目录分组名>" \
165
+ --picker-group "<中转站名> · <显示分组名>"
166
+ ```
167
+
168
+ 用户要求「批量添加并显示为分组」时,必须传 `--picker-group`;不传会向后兼容地落到「自定义模型(BYOK)」总组。不要擅自把 `--group` 原样当显示标题:目录分组可能是晦涩的计费 key,而且一个模型可同时属于多个目录组。
169
+
170
+ - `--group` 与 `--models` 可叠加,取交集。
171
+ - 组名写错会**列出可用组并报错**,不会静默导入空集或错误集合——照着报错里的组名重来即可。
172
+ - 导入是原子的,已存在的稳定 id 自动跳过,重复跑安全。
173
+
174
+ **5. 报给用户看的是取舍,不是 id 列表。** 同一个模型常出现在多个组里,组与组之间差的是**价格倍率和稳定性**(`ratio` 越低越便宜)。当用户只说"帮我加 claude"时,把候选组连同倍率说给他,让他选——而不是替他挑一个贵的或不稳的。
175
+
176
+ > ⚠️ **能拉到 ≠ 能用**:目录说某组支持某模型,只代表"允许",不代表该组当前有活着的通道。用户报某个模型 503/无响应时,那是**分组选错**,换组重导即可,不是 profile 配错。
177
+
178
+ **Coding Plan 套餐(GLM / Kimi Code / MiniMax / 百炼)不走 discover/import**:模型与区域由已启用的套餐插件声明,key 拿到后一条命令建好 `coding-plan-<provider>-*`:`cteno-host-cli profile apply-preset --plugin <glm-coding-plan|kimi-code|minimax-coding-plan|bailian-coding-plan> --api-key-env KEY [--region <id>]`。剩余额度用 `cteno-host-cli plugin quota` 读(vendor `glm`/`kimi`/`minimax`);百炼没有 API-key 用量接口,只能在百炼控制台看,如实告诉用户。
179
+
180
+ ## 网页搜索服务商(websearch 域)
181
+
182
+ `websearch` 工具的服务商由插件(内建 `cteno.websearch`:`cteno` 托管 / `tavily` 自带 Key)声明,默认 auto:已配置 Key → 登录托管 → env `TAVILY_API_KEY`。用户说"用我自己的 Tavily Key""关掉网页搜索"时在这里闭环:
183
+
184
+ ```
185
+ cteno-host-cli websearch providers # selected / effective / 各服务商 available
186
+ request_secret(label:"Tavily API Key", persist:true, name:"TAVILY_API_KEY") # 先 op:list 查是否已有
187
+ cteno-host-cli websearch set-key --provider tavily --api-key secret://vault/TAVILY_API_KEY
188
+ cteno-host-cli websearch set-default --backend tavily # auto | disabled | cteno | tavily
189
+ cteno-host-cli websearch test --query "hello" # 走生效服务商真搜一次
190
+ ```
191
+
192
+ - `set-key` 只接受 `secret://` 引用(或 `--api-key-env` / 隐藏输入,会先封存为本地引用);明文会被拒绝。
193
+ - 指定服务商后不会自动降级:缺 Key 会直接报配置错误。`disabled` 会把 `websearch` 从下一轮工具面移除。
194
+
195
+ ## Jelly 主对话与线程(jelly 域,capability `jelly.v1`)
196
+
197
+ Jelly = 一只带颜色、记忆模式(shared/independent,当前只存值)和固定 workdir 的主对话 Agent。默认 Jelly 用 `~/.cteno/chat/`,懒创建于第一次 `list`/`open`;新 Jelly 平级放在 `~/.cteno/jelly/<名字>/`。线程 = 主对话的子会话,由血缘推出,不另存状态。
198
+
199
+ ```bash
200
+ cteno-host-cli jelly list # 所有 Jelly(id/name/color/memoryMode/workdir/mainThreadId)
201
+ cteno-host-cli jelly open [--jelly <id>] # 确保主对话存在(起始为空,不接管旧聊天)
202
+ cteno-host-cli jelly create --name 工作 --color '#ff8800' --memory-mode independent
203
+ cteno-host-cli jelly update --jelly <id> [--name ..] [--color ..|--clear-color] [--memory-mode ..]
204
+ cteno-host-cli jelly delete <id> # 删除非默认 Jelly(capability jelly.delete.v1,见下)
205
+ cteno-host-cli jelly threads --jelly <id> # 线程:threadId/title/status(running|waiting|done|failed)
206
+ cteno-host-cli jelly suggested-prompts # 已弃用:仅为老客户端保留(capability suggested-prompts.v1),新客户端不渲染
207
+ ```
208
+
209
+ - 推荐问题已下线:客户端不再在输入框上方或空白页展示推荐问题,空白页只留 Jelly 名字标题。skill frontmatter `suggested_prompts` 与插件 manifest `surfaces.suggestedPrompts` 仍可声明(校验兼容),但不再被消费。
210
+ - Jelly 有事会主动在主对话里发一条自己的消息气泡(如 dream 整理记忆后的建议,每天最多 1 条,状态 `voiced`,可用 `memory asks --status voiced` 查);用户在对话里回话即可,Jelly 再 `memory decide <id> accept|reject`。卡片只用于需要批准的事。
211
+
212
+ - `jelly delete <id>`:默认 Jelly 拒删(错误 `jelly_delete_default`)。其余 Jelly:先停掉主对话和线程里在跑的事并归档(只归档,不物理删除),暂停指向它的定时任务/loop,撤回待处理卡片,解绑连接器路由,把 workdir 移到 `~/.cteno/jelly/.trash/<名字>-<时间戳>/`(可找回),最后删 profile。幂等:重复删除返回 `deleted:false`;中途失败可直接重试。
213
+ - 同名 Jelly 会被拒绝;主对话 id 即 `mainThreadId`,可继续用 `session trace` 读。
214
+
215
+ ## 通用 session 迁移(session export / import)
216
+
217
+ `session export` / `session import` 是**按 session 键控**的迁移入口——与 `inbox export/import`(按 connector source 键控)不同,它面向任意 session,不要求 session 绑了 connector。
218
+
219
+ - **导出**:`cteno-host-cli session export <session_id> [--workspace] [--credentials] [--out <file> | --raw-out <zip>]`
220
+ - `--workspace` 把 session 声明的 workdir 打进包(`workspace/**`);不加则只带 transcript/context。
221
+ - `--credentials` 把绑定到该 session 的 connector 凭据密封进包;不加则不带任何 secret。
222
+ - 包(含凭据)**不打印到 stdout**,用 `--out`(base64)或 `--raw-out`(zip)落盘;与 `inbox export` 同一脱敏约定。
223
+ - **导入**:`cteno-host-cli session import --file <file> [--new-session-id <id>] [--overwrite]`
224
+ - **默认用全新 session id 恢复**,避免两台机器出现同 id 的 session(GUI 按 id 聚合会撞);`--new-session-id` 可钉死 id。
225
+ - 绑定的 connector 会 re-point 到新 id 且**默认 paused**,不会自动开始接收。
226
+ - **连接器一键跨机**:`cteno-host-cli inbox migrate-to-machine --source <id> --to <machineId>` —— 把连接器及其挂载对话搬到**站点主机或同账号桌面机**(服务端 `assertUsableTarget` 已放宽接受账号内 `Machine`)。源 daemon 通过 relay 驱动目标,单个 `inbox-migrate-to-machine-complete` RPC 完成 export→reserve→upload→目标 import→pause 本地→resume 目标→commit→cleanup,含回滚。`--machine <id|name>` 可先把 `--to` 解析成机器 id(`cteno-host-cli machines list` 列出账号机器)。
227
+ - **与连接器迁移的区别**:`inbox migrate-to-machine`(source 键控,跨机自动编排)与 `session export/import`(session 键控,手工搬包)是两条路径。普通 session(无 connector)的**跨机自动编排**(`session migrate --to <machine>` 单命令)尚未落地;当前普通 session 跨机仍用 `session export` → 搬包 → `session import`。
228
+
229
+ ## 诊断包与问题上报(diagnostics 域)
230
+
231
+ 用户说"报错了/帮我把日志发给开发者/导出诊断"时在这里闭环。收集默认**全量**(日志、故障现场、会话内容、配置状态、崩溃报告)+ 多层脱敏;凭据文件永不入包。
232
+
233
+ ```
234
+ cteno-host-cli diagnostics incidents [--limit 20] # 自动留存的故障现场(fatal turn error)
235
+ cteno-host-cli diagnostics export [--session <id>] [--turn <id>] [--error-code E_...] [--note "..."] [--out-dir <dir>]
236
+ cteno-host-cli diagnostics upload [同上参数] # 先本地落盘,再加密上传,返回 ticket(CTD-YYYYMMDD-XXXXXX)
237
+ # 可选剔除:--no-logs --no-incidents --no-conversation --no-state --no-crash;--no-pii-mask 关闭个人信息遮盖
238
+ ```
239
+
240
+ - `upload` 的结果永远含本地 `bundle.path`;失败时是 `uploadError`:`diagnostics-not-signed-in` / `diagnostics-upload-not-configured` → 告诉用户把本地 zip 手动发给开发者。
241
+ - 输出里 `redaction` 是按规则计数的脱敏报告;把 ticket 或文件路径原样告诉用户。
242
+ - 开发者侧:`diagnostics keygen` 生成收件密钥对(公钥配到服务器 `DIAGNOSTICS_PUBLIC_KEY`),`diagnostics decrypt --key <secret|@file|env:VAR> --in x.ctdiag --out x.zip` 解密。
243
+ - 错误码见 GUI 错误气泡的"报告问题"按钮(`E_COMPACT_BARRIER_TIMEOUT`、`E_RATE_LIMIT`、`E_NETWORK` 等),同一 turnId 可作 `--turn`。
244
+
245
+ ## Hook、RPC 与判定时刻(capability `hooks-rpc-v1` / `hooks-subscribe-v1`)
246
+
247
+ - `plugin hooks`:所有 Hook 点(含 PreRpc/PostRpc/PostRpcFailure 与连接器投递前的 PreConnectorDeliver)、谁声明了什么,以及 `rpcMethods[]` 每个 RPC 的等级(blockable/observe/exempt)与是否已声明(`declared`)。RPC 被 Hook 拒绝时报错文本是 `Blocked by hook: <理由>`,这是策略结果,不要换路重试。
248
+ - `session invoke-tool <sid> <tool> --input '<json>'`:在该会话里执行一个工具,走正常的执行上限与 PreToolUse/PostToolUse。
249
+ - `decision points`:内置与插件判定时刻、当前模式(off/shadow/active)和记录汇总;`decision records --point <id> [--session <sid>]` 回看明细;`decision set-mode --point <id> --mode off|shadow|active|default` 切换。把 shadow 时刻转 active 前先看记录里的答案和动作是否靠谱。
250
+ - 项目级 Hook 写在 `<workdir>/.cteno/hooks.yaml`,条目格式与插件 manifest 的 `surfaces.hooks` 相同。
251
+
252
+ ## 验证原则
253
+
254
+ - 连接错误先跑对应 target 的 `status`。
255
+ - 命令返回码、日志片段或 `waited:false` 都不等于 Agent 行为完成。
256
+ - 排查 session 以持久化 `session trace` 为权威证据;机器读取长回复时使用完整输出选项,具体参数看 help。
@@ -0,0 +1,23 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cteno
4
+ Portions Copyright (c) 2026 Nous Research (scripts/ and tests/ adapted from the Hermes Agent productivity skills)
5
+ Portions Copyright (c) 2026 MiniMaxAI (references adapted from MiniMax-AI/skills)
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
@@ -0,0 +1,8 @@
1
+ # Sources for the office skills (docx, pptx, xlsx, pdf)
2
+
3
+ - **Scripts and tests**: adapted from Nous Research Hermes Agent `skills/productivity/{docx,powerpoint,xlsx,pdf}` (MIT), upstream commit `bf0203a` (2026-10-22).
4
+ - **Chinese typesetting, design and formatting references**: adapted from MiniMax-AI/skills `minimax-docx`, `pptx-generator`, `minimax-xlsx`, `minimax-pdf` (MIT); `xlsx/scripts/formula_check.py` from minimax-xlsx.
5
+ - **Cteno additions**: `scripts/render.py` (isolated LibreOffice profile, macOS CJK font exposure and substitution), `scripts/cjk_fonts.py` (East Asian font slots), CJK handling in `docx_create.py` / `pptx_create.py` / `pdf_create.py`, 16:9 layout stretch in `pptx_create.py`, all `SKILL.md` texts.
6
+ - Anthropic's document skills (proprietary licence) informed which pitfalls to cover; no text or code was copied from them.
7
+
8
+ Not tracked live: to update, diff the upstream folders above against these copies and re-run each skill's `tests/` plus a render check.
@@ -0,0 +1,69 @@
1
+ ---
2
+ id: docx
3
+ name: docx
4
+ version: "2.0.0"
5
+ description: "Create, read, edit, fill templates in, review (tracked changes/comments) and convert Word .docx documents, with Chinese typesetting rules and a mandatory render-and-look check."
6
+ when_to_use: "Use whenever a Word file is input or output: reports, proposals, contracts, letters, memos, 公文, 合同, 报告, 简历, filling a .docx template, find-and-replace, tracked changes or comments, or reading a .docx's text/tables. Not for PDF-only output (use pdf), spreadsheets (xlsx) or slides (pptx)."
7
+ license: MIT (see LICENSE; scripts adapted from Nous Research and MiniMax, MIT)
8
+ tags: [docx, word, document, report, contract, 公文]
9
+ ---
10
+
11
+ # Word documents (.docx)
12
+
13
+ A `.docx` is a zip of XML parts. Pick the route by task:
14
+
15
+ | Task | Route |
16
+ |---|---|
17
+ | New document | Write a JSON spec → `${SKILL_DIR}/scripts/docx_create.py` (python-docx). For layouts the spec can't express, write python-docx code directly |
18
+ | Fill a `{{token}}` template | `${SKILL_DIR}/scripts/docx_template.py` |
19
+ | Change an existing file | `${SKILL_DIR}/scripts/docx_edit.py` (replace text across runs, edit table cells, insert/delete paragraphs, styles, TOC, page-number fields) |
20
+ | Read | `${SKILL_DIR}/scripts/docx_read.py FILE --text | --structure | --styles | --images DIR | --revisions` (or `pandoc -t markdown FILE`) |
21
+ | Tracked changes / comments | `${SKILL_DIR}/scripts/docx_revisions.py`, `${SKILL_DIR}/scripts/docx_comments.py` — read `${SKILL_DIR}/references/revisions-and-comments.md` first |
22
+ | Package health | `${SKILL_DIR}/scripts/docx_validate.py FILE` |
23
+ | Look at it | `${SKILL_DIR}/scripts/render.py FILE` → PNG per page |
24
+ | `.doc` / `.rtf` / `.odt` in | `soffice --headless --convert-to docx FILE` first |
25
+
26
+ Every script takes `--help`, prints JSON, exits non-zero on failure. Always call scripts by the absolute paths shown here (not a copy found elsewhere on disk).
27
+ Dependencies: `python3 -m pip install python-docx lxml` (once). For rendering: LibreOffice + poppler (`render.py` prints the install command if missing).
28
+
29
+ ## Before writing: decide the document, not just the text
30
+
31
+ 1. **Type and reader.** Contract, report, proposal, letter, 公文, resume, academic paper — each has a convention (below). Follow the user's template or house style over anything here.
32
+ 2. **Paper and margins.** China/Europe: A4 (210×297 mm). US: Letter. Margins 25 mm default; 公文 has fixed ones.
33
+ 3. **Hierarchy.** Real heading styles (`heading` blocks / `Heading 1..3`), never bold body text pretending to be a heading — the TOC, navigation pane and accessibility depend on them.
34
+ 4. **Every number and name** comes from the user or a cited source; mark unknowns as `【待确认】` instead of inventing.
35
+
36
+ ## Chinese documents (read `${SKILL_DIR}/references/cjk_typography.md` for details)
37
+
38
+ - **Fonts need the East Asian slot.** Word picks CJK glyph fonts from `w:eastAsia`, not the Latin font. `docx_create.py` sets it automatically when the spec contains CJK (body 宋体 SimSun, headings 黑体 SimHei; override with `"cjk_fonts": {...}`). For files built any other way, run `python3 ${SKILL_DIR}/scripts/cjk_fonts.py out.docx` before delivery.
39
+ - **Sizes by 字号**: 二号 22pt, 三号 16pt, 四号 14pt, 小四 12pt, 五号 10.5pt. Body 小四 or 五号 for business; line spacing 1.5 (or fixed 28 pt for 公文).
40
+ - **First-line indent 2 characters** for body paragraphs (`firstLineChars=200`); no indent for headings, tables, lists.
41
+ - **Numbering** 一、 →(一)→ 1. →(1). Full-width punctuation in Chinese text (,。:;“”), half-width inside English and numbers.
42
+ - **公文 (GB/T 9704)**: A4, margins top 37 / bottom 35 / left 28 / right 26 mm, title 小标宋 二号 centered, body 仿宋_GB2312 三号 fixed 28 pt, 一级标题 黑体, 二级 楷体, page number `-1-` 宋体 四号 bottom. Use `--body FangSong_GB2312 --heading SimHei` with `cjk_fonts.py`.
43
+ - **Contracts**: 甲方/乙方 block, numbered articles (第一条…), defined terms bold on first use, signature/seal table at the end, page "第 X 页 共 Y 页" footer, no orphaned signature block on its own page.
44
+
45
+ ## Gotchas (the model knows python-docx; these are the traps)
46
+
47
+ - **Never put `\n` in text** — make separate paragraphs. A literal `•` makes a double bullet; use `bullet_list` / `List Bullet`.
48
+ - **Replacing text**: Word splits a visible phrase across many runs (spell-check, revision ids). Use `docx_edit.py replace` (merges same-format runs first); `paragraph.text = ...` wipes formatting.
49
+ - **Tables**: set explicit column widths; header row bold and repeated on page break; numbers right-aligned; cells vertically centered with zero first-line indent and tight spacing (body styles otherwise leak in and text sits at the cell top). `docx_create.py` does this; in hand-written python-docx set `cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER` and reset each cell paragraph's indent/spacing. Don't use a table as a horizontal rule.
50
+ - **Fields (TOC, PAGE, NUMPAGES) are computed by Word/LibreOffice on open.** A TOC built by the script shows placeholder text until updated; when the deliverable must show the TOC, update fields by converting through LibreOffice (`render.py --pdf-only` shows the real result) and say so.
51
+ - **Images** need a width; keep aspect ratio; ≥150 dpi for print.
52
+ - **Editing someone's file**: keep their styles, numbering and section breaks; change only what was asked; save to a new name unless told to overwrite.
53
+ - **Untrusted input**: a `.docx` from outside may contain macros/external links; never execute embedded content.
54
+
55
+ ## Design (for documents you create; see `${SKILL_DIR}/references/design_principles.md`)
56
+
57
+ - One body font, one heading font; 3 heading levels at most; generous margins and paragraph spacing (6–12 pt after) beat decoration.
58
+ - One accent color at most, used for headings or table headers — not for body text.
59
+ - No cover page, TOC, or header/footer unless the document is long (> 5 pages) or the type requires it.
60
+ - Business documents: conclusion or summary first, then supporting sections.
61
+
62
+ ## Verify before delivery (required)
63
+
64
+ 1. `python3 ${SKILL_DIR}/scripts/docx_validate.py out.docx` — must report `"ok": true`.
65
+ 2. `python3 ${SKILL_DIR}/scripts/docx_read.py out.docx --structure` — headings, order and tables match the plan; grep for leftovers: `{{`, `TODO`, `xxx`, `lorem`.
66
+ 3. `python3 ${SKILL_DIR}/scripts/render.py out.docx` and **open every page image** (read tool). Look for: CJK shown in a wrong/fallback font or boxes, text running off the page, tables wider than the page, orphan headings at a page bottom, an empty last page, wrong page numbers.
67
+ 4. Fix in the generator and re-render only what changed. If rendering tools are missing, say the visual check was not done.
68
+
69
+ Deliver the `.docx` (and a PDF only if asked).