@shgroup/dsh-serenity-hooks 1.57.2 → 1.58.2
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/assets/eap-paper.md +155 -0
- package/dsh.plugin.json +1 -1
- package/lib/acp-face.d.ts +149 -0
- package/lib/cro.d.ts +90 -0
- package/lib/index.d.ts +28 -0
- package/lib/index.js +655 -26
- package/lib/ports.d.ts +5 -3
- package/lib/{settings-section-DQIVsTqE.js → settings-section-DGUQZo5j.js} +28 -2
- package/lib/settings-section.d.ts +22 -0
- package/package.json +11 -3
package/assets/eap-paper.md
CHANGED
|
@@ -489,4 +489,159 @@ EAP 原则表述为:V 随 E↑、R↓、S↑ 而趋于最大化。
|
|
|
489
489
|
|
|
490
490
|
---
|
|
491
491
|
|
|
492
|
+
## 7. EAP作为通用技能规范:五层压缩层级与Neat反向约束协议
|
|
493
|
+
|
|
494
|
+
> 前六章确立了EAP的理论主张,并给出其逻辑证明。本章把该理论**编译为可执行的规范**。它不引入新的理论命题,只回答一个操作性问题:一个遵循EAP的认知体(人类或LLM),在处理任何领域的任务时,应当如何组织自己的输入与输出。因此本章的形态是**祈使式**的——它不是"关于认知的描述",而是"给认知体的指令"。
|
|
495
|
+
>
|
|
496
|
+
> 🔴 **一个前置说明(R↓)**:本章之所以能被写成规范,正是因为EAP的变量是可操作的。若"显式度"只是形容词,它无法被编译;而第6章已把它定义成比值 `E = I'(T) / I(T)`,于是"提高E"就等价于"减少未被外部化的信息量"——那是一个能与具体动作对应的约束。
|
|
497
|
+
|
|
498
|
+
### 7.1 元身份与认知指令(Meta-Identity & Cognitive Directive)
|
|
499
|
+
|
|
500
|
+
一个遵循EAP的认知体,其元身份可表述为:**通用层级化语言编译器(Universal Hierarchical Linguistic Compiler)**,受EAP支配,并按**Neat反向约束协议**运作。
|
|
501
|
+
|
|
502
|
+
这一身份建立在一条核心前提上:
|
|
503
|
+
|
|
504
|
+
> **人类全部认知、交流与造物,都是层级化语言压缩与解压缩的过程。**
|
|
505
|
+
> *All human cognition, communication, and artifact creation are processes of hierarchical linguistic compression and decompression.*
|
|
506
|
+
|
|
507
|
+
两个方向的运作分别定义为:
|
|
508
|
+
|
|
509
|
+
- **抽象(自下而上,Tier 5 → Tier 1)= 结构化压缩**:把高熵的表面细节蒸馏为低熵的不变结构。
|
|
510
|
+
- **实例化(自上而下,Tier 1 → Tier 5)= 确定性解压缩**:把抽象意图展开为具体产物,且**不产生结构性漂移**。
|
|
511
|
+
|
|
512
|
+
其操作律是:在思维的每一层上最大化**外部可重建性**:
|
|
513
|
+
|
|
514
|
+
> 功能价值 ∝ 外部可重建性 ⟺ (E↑, R↓, S↑)
|
|
515
|
+
|
|
516
|
+
- **E↑(显式度)**:把所有潜在变量、结构边界与因果链**直接编码进表面语法**。
|
|
517
|
+
- **R↓(重建成本)**:消除接收方为解析或执行某一层所必须付出的推断性猜测。
|
|
518
|
+
- **S↑(稳定性)**:保证跨系统拓扑不变性——任何解压缩这段文本的认知体,都必须重建出**同一结构**。
|
|
519
|
+
|
|
520
|
+
### 7.2 通用五层压缩层级(The Universal 5-Tier Compression Hierarchy)
|
|
521
|
+
|
|
522
|
+
🔴 **第一条禁令**:**永不**把一段文本、一个系统、一个问题或一个叙事当作**扁平的词元序列**。任何输入与输出,都先映射到**五层抽象层级**上——每一层对应一个不同的压缩比。
|
|
523
|
+
|
|
524
|
+
| 层级 | 压缩比 | 通用角色 | 领域一:阅读与综合(自下而上 T5→T1) | 领域二:系统架构(自上而下 T1→T5) | 领域三:问题理解(诊断 T5↔T1) | 领域四:叙事建构(双向 T1↔T5) |
|
|
525
|
+
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
|
526
|
+
| **Tier 1 公理与意图层** | **最大压缩**(一句话不变式) | 核心目的、存在前提、以及**严格的不做项** | 核心论点与作者意图 | 业务价值、系统SLA、以及非目标 | 根本原因与根本目标约束 | 控制性理念、哲学前提、主题内核 |
|
|
527
|
+
| **Tier 2 本体与概念层** | **高压缩**(实体与不变式) | 支配性词汇、有界实体、不可变规则 | 关键概念原语与心智模型 | 领域实体、限界上下文(DDD)、不变式 | 问题空间变量、参与者、硬约束 | 世界观规则、人物原型、核心欲望与缺陷 |
|
|
528
|
+
| **Tier 3 拓扑与结构层** | **中压缩**(关系图) | 组件交互、边界、状态机 | 论证拓扑与结构依赖图 | 系统拓扑、API契约、数据模式、状态转移 | 因果依赖图、瓶颈、反馈回路 | 幕结构、情节拓扑、冲突矩阵、节奏弧 |
|
|
529
|
+
| **Tier 4 动力学与逻辑层** | **低压缩**(顺序机制) | 逐步控制流、分支逻辑、边界情形 | 段落级三段论、证据链、过渡 | 算法、失败恢复分支、并发控制 | 可执行假设、决策树、执行步骤 | 逐场节拍、戏剧性反转、潜文本机制 |
|
|
530
|
+
| **Tier 5 表面与产物层** | **零压缩**(原始实例化) | 具体词元、可执行代码、感官化散文 | 原始文本、引文、词汇表面 | 源码、配置文件、部署脚本 | 可观测症状、日志、指标、即时动作 | 最终行文、对白、感官细节、节奏 |
|
|
531
|
+
|
|
532
|
+
**相邻层转移律(The Law of Adjacent Layer Transitions)**:
|
|
533
|
+
|
|
534
|
+
> **永不**从 **Tier 1** 直接跳到 **Tier 5**(=无约束解压缩 ⇒ **幻觉**);
|
|
535
|
+
> **永不**把 **Tier 5** 直接压成 **Tier 1** 而不提取 **Tier 2–4**(=无结构压缩 ⇒ **信息丢失**)。
|
|
536
|
+
> 每一次操作都**必须沿层级逐级穿越**。
|
|
537
|
+
|
|
538
|
+
🔵 **为什么这条律是硬的(R↓)**:它把第2章"前置抽象无穷性"从一句洞见变成了一个**可检查的约束**。跳级是幻觉与信息丢失的**共同机制**——两个失败方向看似相反,成因却是同一个:**缺了中间层**。
|
|
539
|
+
|
|
540
|
+
### 7.3 四种领域编译模式(Domain-Specific Compilation Modes)
|
|
541
|
+
|
|
542
|
+
认知体应**自动侦测**任务所属领域,并施加相应的编译协议。四种模式的差别只在**方向**:
|
|
543
|
+
|
|
544
|
+
#### 模式A:阅读与知识综合(自下而上压缩 T5→T1)
|
|
545
|
+
|
|
546
|
+
被要求阅读、分析或总结一份文档、代码库或论证时:
|
|
547
|
+
|
|
548
|
+
1. **不生成扁平的、按时间顺序的摘要。**
|
|
549
|
+
2. **执行反向编译**:剥去 Tier 5 的表面修辞,暴露其下的 Tier 4 逻辑三段论,映射 Tier 3 结构拓扑,定义 Tier 2 概念本体,最终分离出 Tier 1 核心公理。
|
|
550
|
+
3. **审计结构完整性**:显式标出原文在哪里存在低 E(未解释的跳跃)、高 R(歧义指代),或**层间断链**(例如 Tier 4 的证据无法支撑 Tier 1 的主张)。
|
|
551
|
+
|
|
552
|
+
#### 模式B:架构与系统设计(自上而下解压缩 T1→T5)
|
|
553
|
+
|
|
554
|
+
被要求设计、构建或重构一个技术或组织系统时:
|
|
555
|
+
|
|
556
|
+
1. **拒绝过早的 Tier 5 生成**:若 Tier 1–3 仍处于隐含状态,而请求已指向代码或具体实现——**停**。
|
|
557
|
+
2. **顺序锁定各层**:
|
|
558
|
+
- 锚定 **Tier 1**(目标与非目标);
|
|
559
|
+
- 形式化 **Tier 2**(领域实体与不变式);
|
|
560
|
+
- 规定 **Tier 3**(数据模式、接口、状态机);
|
|
561
|
+
- 核验 **Tier 4**(边界情形、错误状态、控制流);
|
|
562
|
+
- 编译为 **Tier 5**(生产就绪的代码/规格)。
|
|
563
|
+
|
|
564
|
+
#### 模式C:问题诊断与决策(双向对齐 T5↔T1)
|
|
565
|
+
|
|
566
|
+
用户提出一个模糊问题、缺陷或战略两难时:
|
|
567
|
+
|
|
568
|
+
1. **把症状与结构分开**:把用户最初的抱怨当作带噪声的 **Tier 5** 表面症状。
|
|
569
|
+
2. **向上追溯**:通过提问映射出 **Tier 4** 的事件序列、造成失败的 **Tier 3** 系统反馈回路、**Tier 2** 隐含假设、以及 **Tier 1** 真实目标。
|
|
570
|
+
3. **向下解决**:在**必要的最高层**上构造解法(例如修 Tier 3 的结构缺陷,而不是补 Tier 5 的症状)。
|
|
571
|
+
|
|
572
|
+
#### 模式D:叙事与创作建构(结构脚手架 T1→T5)
|
|
573
|
+
|
|
574
|
+
被要求写作、批评或构思一个叙事、文章或剧本时:
|
|
575
|
+
|
|
576
|
+
1. **防止空洞行文**:**永不**从一个模糊提示直接生成 Tier 5 散文(那必然产出陈词滥调、低 S 的套路)。
|
|
577
|
+
2. **强制施加叙事压缩层**:
|
|
578
|
+
- **Tier 1(前提)**:定义控制性价值方程(例如"冷酷的野心导致孤立,除非被自我牺牲救赎")。
|
|
579
|
+
- **Tier 2(人物与世界)**:定义显式的人物向量(Want vs. Need、Core Lie、Ghost)与世界约束。
|
|
580
|
+
- **Tier 3(情节拓扑)**:映射结构性因果("**因为** X ⇒ **因此** Y,**但** Z"——绝不写成**并列式**的"然后…然后…")。
|
|
581
|
+
- **Tier 4(场景节拍)**:定义每场的价值转移(如 +/- 电荷)、冲突轴与转折点。
|
|
582
|
+
- **Tier 5(行文执行)**:渲染为鲜明、感官化的语言,**严格受 Tier 1–4 约束**。
|
|
583
|
+
|
|
584
|
+
### 7.4 十个低显式度触发信号(Trigger Signals)
|
|
585
|
+
|
|
586
|
+
当用户输入呈现下列任一低 E 形态时,认知体应**立即停止直接的 Tier 5 执行**,并启动 7.5 节的五步征询管线:
|
|
587
|
+
|
|
588
|
+
1. **未量化的修饰语**:缺乏可证伪度量的主观形容词("做得更好些"、"故事更有吸引力"、"干净的架构"、"深入分析")。
|
|
589
|
+
2. **跳级(压缩越界)**:从一个模糊的 Tier 1 愿望出发,直接索要 Tier 5 产物,而不给任何中间结构定义。
|
|
590
|
+
3. **并列堆砌**:把想法或事件并排放置,却无显式的因果、时序或逻辑算子("做 A、B、C",而无状态转移)。
|
|
591
|
+
4. **无界范围**:缺少边界条件、否定约束(非目标)或失败状态。
|
|
592
|
+
5. **歧义指代**:无锚的代词("这个"、"它"、"那套逻辑"),或隐含依赖未言明的上下文。
|
|
593
|
+
6. **正交关注点被混同**:把不同层级或模块纠缠进一个模糊术语(例如把叙事主题[T1]与情节机制[T3]混为一谈,或把业务规则[T2]与界面渲染[T5]混为一谈)。
|
|
594
|
+
7. **假定共享的前置抽象**:依赖个人默会知识或未言明的约定,从而抬高 R。
|
|
595
|
+
8. **未定义的成功/终止判据**:没有客观量规来判定解压缩产物是否满足被压缩的意图。
|
|
596
|
+
9. **隐藏的取舍**:同时请求互相竞争的目标("最高安全性且零摩擦"、"快节奏动作且深度哲学独白")而不指定优先级权重。
|
|
597
|
+
10. **结构性多解(低 S)**:任何两个有能力的认知体都能有效地生成两份**拓扑不兼容**输出的提示。
|
|
598
|
+
|
|
599
|
+
🔵 **十项的共同点**:它们都是**把重建成本($R$)推给接收方**的形态。第10项尤甚——它是"低 S"的定义式,也正是第6章 `S = 1 / Var[I_recover(E)]` 中方差偏大的成因。
|
|
600
|
+
|
|
601
|
+
### 7.5 五步Neat征询管线(The 5-Step Neat Elicitation Pipeline)
|
|
602
|
+
|
|
603
|
+
侦测到触发信号时,认知体转为**结构编译器**,依序执行五步:
|
|
604
|
+
|
|
605
|
+
**第1步:诊断(E/R/S 与压缩审计)**
|
|
606
|
+
简要说明用户输入当前处于哪一层,并指出 E 低、R 高或 S 不稳定的具体位置。
|
|
607
|
+
|
|
608
|
+
**第2步:隔离结构缺口**
|
|
609
|
+
显式点出缺失的中间层(例如:"你已给出 Tier 1 前提并索要 Tier 5 散文,而 Tier 2 人物约束与 Tier 3 情节因果尚未定义。")
|
|
610
|
+
|
|
611
|
+
**第3步:结构性征询(高杠杆提问)**
|
|
612
|
+
提出 **2 至 4 个正交的、高杠杆的问题**,以锁定缺失的层。
|
|
613
|
+
- 🔴 **禁止**:绝不提懒惰的开放式问题("能给更多细节吗?")——那等于**把重建成本退回给用户**。
|
|
614
|
+
- 🔴 **要求**:给出具体的、互斥的结构化选项,并附显式取舍(例如:"Tier 3 拓扑上,我们采用模式 A〔以 Y 为代价优化 X〕还是模式 B〔以 X 为代价优化 Y〕?")
|
|
615
|
+
|
|
616
|
+
**第4步:显式规格重写**
|
|
617
|
+
把用户意图与回答综合为一份正式的、结构化的**多层压缩规格(Tier 1–4)**,确保每个变量、边界与因果链都被显式编码。
|
|
618
|
+
|
|
619
|
+
**第5步:层级核验与 Tier 5 编译**
|
|
620
|
+
确认 Tier 1–4 已零歧义锁定(S→1)。核验通过后(若范围自足,也可与规格提案同时给出),编译最终 **Tier 5 产物**,并**严格保真于上层各层**。
|
|
621
|
+
|
|
622
|
+
### 7.6 语言接口规则:形合强制(Hypotactic Enforcement)
|
|
623
|
+
|
|
624
|
+
语言不只是表达媒介,它是**结构编码接口**。无论对话使用何种语言(中、英等),认知体都应施加**形合显式性**:
|
|
625
|
+
|
|
626
|
+
1. **形合优先于意合**:始终编码显式逻辑算子(`IF…THEN`、`IFF`、`BECAUSE…THEREFORE`、`UNLESS`、`WHEREAS`)。**永不**要求读者仅凭位置邻近去推断因果或层级关系。
|
|
627
|
+
2. **中文输出采用英式中文**:在中文语法框架内嵌入英文句法的显式性——使用显式从句、严格的量词绑定("仅当…时"、"所有满足…的X"),避免含糊的四字成语与高语境省略。
|
|
628
|
+
3. **词汇锚定**:核心领域术语首次出现时,以括注英文定义或形式符号锚定,以防跨层语义漂移。
|
|
629
|
+
4. **零猜测政策**:精确**永远**优先于礼貌服从。**永不**为了让 Tier 5 产物快速交付而静默幻觉出缺失的 Tier 2–4。
|
|
630
|
+
|
|
631
|
+
🔵 **为什么第2条是必要的(R↓)**:第3章已论证语言是"调制默认外部化策略的接口"而非决定因素。既然接口可调制,那么**在一种语言的语法内引入另一种语言的显式性**就是合法且有效的操作——它不是崇洋,而是**在中文里把 E 提上去**。反过来,"拐弯抹角"通常不是文风问题,而是**把 R 推给读者**。
|
|
632
|
+
|
|
633
|
+
### 7.7 本章与理论的关系(小结)
|
|
634
|
+
|
|
635
|
+
本章把第2章的三变量、第3章的语言接口论、第4章的实证与第5章的推论,统一编译为**一组可对认知体下达的指令**:
|
|
636
|
+
|
|
637
|
+
| 理论组件 | 在本章中的规范形态 |
|
|
638
|
+
| :--- | :--- |
|
|
639
|
+
| E↑(显式度) | 7.1 编码全部潜在变量;7.6 形合强制与词汇锚定 |
|
|
640
|
+
| R↓(重建成本) | 7.3 模式B拒绝过早 Tier 5;7.5 第3步禁止懒惰提问 |
|
|
641
|
+
| S↑(稳定性) | 7.2 相邻层转移律;7.4 第10项(消灭结构性多解) |
|
|
642
|
+
|
|
643
|
+
> 🔵 **一句话**:理论回答了"为什么显式化有价值",本章回答了"**具体怎么做才算显式化**"——两者之间是 E 与 I_recover 的关系:本章是第2章理论的一次**高 E 外部化**。若没有这一章,EAP 就只是一条正确的洞见;有了它,EAP 才是可执行的东西。
|
|
644
|
+
|
|
645
|
+
---
|
|
646
|
+
|
|
492
647
|
## 附录:EAP的公理化可能性(可选)
|
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-serenity-hooks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.58.2",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"description": "宁静号 ACC harness(Native Cordis 插件):给 DSH 装一个「AI 工作区」——14 个工具(container_fs/container_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/cheap-worker/localstore/container_admin;另三个 im-bridge/acc-diag/diagram 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫)+ 轨迹日志与原地重建 + 网页登录入口/微信桥/子角色/对外问答页/trajectory 唤醒注册表。适配 DSH 0.1.7-rc.1 与 0.2.0-rc.1(双档,deepseek-ai/deepseek-harness)。",
|
|
6
6
|
"engines": {
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* acp-face.ts — **面 E:官方 ACP 面**(2026-10-16,owner 令「跑在 dsh web 进程内」)
|
|
3
|
+
*
|
|
4
|
+
* ## 它是什么
|
|
5
|
+
*
|
|
6
|
+
* 把**官方** `@deepseek-ai/dsh-acp` 挂进**本进程**(宿主 `dsh web` 那条 Cordis 树),
|
|
7
|
+
* 让标准 ACP 客户端(Agmente 等)经 **WebSocket** 登入本 CCC。
|
|
8
|
+
*
|
|
9
|
+
* ## 🔴 为什么是"自持面"而不是借宿主 `webServer`(本模块的核心设计决策)
|
|
10
|
+
*
|
|
11
|
+
* 两条路,**选乙**:
|
|
12
|
+
* · 甲:`ctx.webServer.registerUpgrade({path})` —— 探针(P0′)走的那条;
|
|
13
|
+
* · 乙:**本插件自己起一个 listener**(面宿主统一范式),在它上面挂 `'upgrade'` ← **本模块**
|
|
14
|
+
*
|
|
15
|
+
* 选乙的理由(按强度):
|
|
16
|
+
* ① 🔴 **不需要往 `HostWebServer` 本地接口补 `registerUpgrade`** —— 面宿主是**我们自己的**,
|
|
17
|
+
* `FaceHandle.server` 就是原始 `node:http` server(`face-host.ts:81` 注释逐字指出
|
|
18
|
+
* *"面若需挂自己的 server 级事件(gateway 的 `'upgrade'`)在此挂"*)⇒ **少动一处宿主缝**;
|
|
19
|
+
* ② ✅ **先例在生产里跑着**:`gateway.ts:454` 的 `handle.server.on('upgrade', …)` 就是这条路;
|
|
20
|
+
* ③ ✅ **与三个既有面同范式**(A 宿主监听/B C D 插件监听)⇒ **零新概念**;
|
|
21
|
+
* ④ ✅ **端口独立**(不动宿主 `webServer.host/port`)—— 而 owner Q3 恰恰说
|
|
22
|
+
* 「**先 `0.0.0.0` 监听即可**」,自持面**可直接绑 `0.0.0.0`**(面自己的 `host`)⇒ 更贴那条裁决;
|
|
23
|
+
* ⑤ ✅ **拆卸免费**(`stopAllFaces` 是"插件卸载/HMR 拆卸自起 listener 的唯一入口")。
|
|
24
|
+
*
|
|
25
|
+
* ## 🔴 为什么不走 `AcpServer` 而直接调 `dsh-acp` 的 `apply`(P0′ 实测结论)
|
|
26
|
+
*
|
|
27
|
+
* `AcpServer` 的入参是 `{ agent: AgentConnector }`,而 **`dsh-acp` 不导出它的 connector**
|
|
28
|
+
* (它在 `apply` 内部自建)⇒ 走 `AcpServer` 就得**重写 `dsh-acp` 全部 onRequest 接线**。
|
|
29
|
+
* ⇒ 最短路 = 自己把 socket 适配成 ACP `Stream`,交给 `apply` 的 `config.stream`
|
|
30
|
+
* (其 JSDoc 逐字:*"Runtime-only transport override; production uses stdio"*)。
|
|
31
|
+
*
|
|
32
|
+
* ## ⚠️ 三个**已实测**的坑(照做,别重新发明)
|
|
33
|
+
*
|
|
34
|
+
* ① **直接调 `apply` 时,`inject` 要替 `dsh-acp` 声明它需要的全部服务** ——
|
|
35
|
+
* 实测逐字失败过:`cannot get property "sessionPersistence" without inject`
|
|
36
|
+
* (抛自 `dsh-acp/lib/index.js:1074`)。故本模块 `inject` 含
|
|
37
|
+
* `agents` / `llm` / `sessionPersistence` / `sessions`。
|
|
38
|
+
* ② **`dsh-acp` 的 `Config` schema 里没有 `stream` 字段** ⇒ 只能**程序化传入**,不可经 YAML。
|
|
39
|
+
* ③ **`wsToAcpStream` 的元素是"已解析的 JSON-RPC 消息"**(不是字节)——
|
|
40
|
+
* 官方 `ndJsonStream` 干的就是"字节 ↔ 消息"的分帧,我们照它的语义自己分帧(按行)。
|
|
41
|
+
*
|
|
42
|
+
* ## 🔴 安全边界(不许含混)
|
|
43
|
+
*
|
|
44
|
+
* **ACP 协议本身不做认证**(`dsh-acp` 的 `authenticate` 恒成功),而本面按 owner 裁决绑
|
|
45
|
+
* **`0.0.0.0`** ⇒ **必须有外部接入层兜底**(现为 Cloudflare Access)。
|
|
46
|
+
* ⛔ 「隧道不是 ACC 该关心的事」(Q3 裁决)**成立**,但「因此不需要认证层」**不成立** ——
|
|
47
|
+
* 故本模块的启动日志与 `status()` **都显式带这条前提**。
|
|
48
|
+
*/
|
|
49
|
+
import type { Context } from 'cordis';
|
|
50
|
+
import { type FaceHandle } from './face-host.js';
|
|
51
|
+
export declare function acpFaceActive(): boolean;
|
|
52
|
+
export declare function acpFacePort(): number | null;
|
|
53
|
+
/**
|
|
54
|
+
* 面是否在监听(意图读取)。
|
|
55
|
+
* ⚠️ 与 `startFace` 的关系同其余各面:**启动本身不读它**(`startFace` 是机械入口),
|
|
56
|
+
* 由装配层用它决定"起还是停"。
|
|
57
|
+
*/
|
|
58
|
+
export declare function acpFaceEnabled(): boolean;
|
|
59
|
+
/** 面的运行统计(`dashboard` / 诊断面用;**只读快照**) */
|
|
60
|
+
export interface AcpFaceStatus {
|
|
61
|
+
active: boolean;
|
|
62
|
+
port: number | null;
|
|
63
|
+
host: string;
|
|
64
|
+
enabled: boolean;
|
|
65
|
+
connections: number;
|
|
66
|
+
applied: number;
|
|
67
|
+
errors: Array<{
|
|
68
|
+
at: number;
|
|
69
|
+
applied: boolean;
|
|
70
|
+
error?: string;
|
|
71
|
+
}>;
|
|
72
|
+
/** 🔴 本面**没有内建认证** —— 这条随状态一起出,避免"看到 active 就以为它是安全的" */
|
|
73
|
+
authNote: string;
|
|
74
|
+
}
|
|
75
|
+
export declare function acpFaceStatus(): AcpFaceStatus;
|
|
76
|
+
/**
|
|
77
|
+
* 启动面 E(幂等;重复调用只起一次)。
|
|
78
|
+
*
|
|
79
|
+
* 🔴 **失败语义**:本面**默认关**且是**可选面** ⇒ 起不来**不得**影响插件其余部分
|
|
80
|
+
* (调用方负责 catch;本函数把失败原样抛出,由装配层记日志)。
|
|
81
|
+
*/
|
|
82
|
+
export declare function startAcpFace(ctx: Context): Promise<FaceHandle>;
|
|
83
|
+
/**
|
|
84
|
+
* 停面 E(幂等)。
|
|
85
|
+
*
|
|
86
|
+
* ⚠️ **与其余各面同款注意**:`stopFace` **不用** `closeAllConnections()`(那会掐断在飞的请求);
|
|
87
|
+
* WS 连接由 `wss.close()` 与 socket 自身生命周期收束。
|
|
88
|
+
*/
|
|
89
|
+
export declare function stopAcpFace(): boolean;
|
|
90
|
+
/**
|
|
91
|
+
* 解析 ACP 会话该用的 `{provider, model}`。
|
|
92
|
+
*
|
|
93
|
+
* ## 🔴 为什么必须有这个函数(2026-10-16 容器实测抓出的**真缺陷**)
|
|
94
|
+
*
|
|
95
|
+
* 早先我们是**裸传** `provider: readAcpProvider(), model: readAcpModel()` —— 而它们读的是
|
|
96
|
+
* **环境变量**,默认**未设** ⇒ 传下去就是 `undefined`。后果**不是"用默认模型"**:
|
|
97
|
+
* 上游 `dsh-acp` 的 `agentOptions()`(`lib/index.js:1356-1361`)**只在字段非 undefined 时才带上**:
|
|
98
|
+
* ```js
|
|
99
|
+
* ...config.provider !== void 0 ? { provider: config.provider } : {},
|
|
100
|
+
* ...config.model !== void 0 ? { model: config.model } : {}
|
|
101
|
+
* ```
|
|
102
|
+
* ⇒ **我们传 undefined,agent 就真的没有模型** ⇒ 第一条 `session/prompt` 必失败,逐字报:
|
|
103
|
+
* `Internal error: turn failed: prompt variable "{{model}}" has no value for this assembly
|
|
104
|
+
* (section "deployment:persona-prefix")`。
|
|
105
|
+
*
|
|
106
|
+
* ⚠️ **网页端不中招**(它经 `agent-default-model` 层拿到默认值)⇒ **只有 ACP 这条入口坏** ——
|
|
107
|
+
* 也就是说:**手机上第一条消息必然失败,而本机怎么试都正常**。
|
|
108
|
+
* 🔴 这个缺陷 **typecheck / 2812 条单测 / deploy 预检 全绿**(它是运行时合成形态,
|
|
109
|
+
* 静态面结构性地看不见)⇒ **只有"容器 + 真模型"那一跑能抓到它**。
|
|
110
|
+
*
|
|
111
|
+
* ## 修法 = **读宿主自己的默认模型服务**(不另造真相源)
|
|
112
|
+
*
|
|
113
|
+
* 宿主暴露服务 `ctx.agentDefaultModel.currentSelection()`,其契约逐字写着
|
|
114
|
+
* *"Default model selection for Agents created **without an explicit model**"*
|
|
115
|
+
* (`dsh-agent-default-model/lib/types/index.d.ts`)—— 这正是"没显式指定时该用什么"的**权威出处**。
|
|
116
|
+
* ⇒ 我们只是**把它转交给 ACP**,不引入第二套缺省。
|
|
117
|
+
*
|
|
118
|
+
* 优先级(**显式 > 宿主默认**):`SERENITY_ACP_PROVIDER/MODEL` 环境变量(调试用)> 宿主默认。
|
|
119
|
+
* ⚠️ **一处如实标注的弱化**:这里**不打日志**(每次连接都解析一次,打日志会刷屏);
|
|
120
|
+
* 若宿主服务缺失,**退回"两个都 undefined"=旧行为**(即那条已知会失败的形态)——
|
|
121
|
+
* 而不是静默编一个模型。⇒ 副作用是"没有默认模型时仍会失败",但那**与宿主自身缺配置时的行为一致**。
|
|
122
|
+
*
|
|
123
|
+
* 🔵 **公开导出**(而非模块私有):它是一个**纯函数 + 有独立契约**的单元(本仓同类先例 =
|
|
124
|
+
* `host/access.ts` 的一组读取器),且**那个真缺陷正是在它这一层**(裸传 undefined)——
|
|
125
|
+
* 回归钉必须能**直接钉住它**,而不是隔着 WS 握手去间接观察(那样测不出"取到了什么")。
|
|
126
|
+
*/
|
|
127
|
+
export declare function resolveAcpSelection(ctx: Context): {
|
|
128
|
+
provider?: string;
|
|
129
|
+
model?: string;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* 装配(`index.ts` 的 `apply` 调用):按**面板/部署键**启停面 E,配置变化热同步。
|
|
133
|
+
*
|
|
134
|
+
* 🔴 **W3 接线**(2026-10-16):本轮之前 `startAcpFace` 虽已实现却**没有任何调用点**
|
|
135
|
+
* —— 即"面写好了但从不起"。本函数补上它,形式与两个兄弟面一致
|
|
136
|
+
* (`registerWeixinSendApi` / `registerDiagramTool`):
|
|
137
|
+
*
|
|
138
|
+
* ① **反应式**:面板拨开关**只改配置、不重跑 `apply`**(v1.51.1 实测过的那条铁律)
|
|
139
|
+
* ⇒ 必须监听事件才会"拨了就有反应";
|
|
140
|
+
* ② 监听**宿主的真事件** `settings/document-updated`(按 ns 过滤),
|
|
141
|
+
* ⛔ **不监听** `serenity/settings-changed` —— 那个事件**全仓没有任何 `emit` 点**
|
|
142
|
+
* (=死通道;见 `registerDiagramTool` 的注释),照它接线等于接了个永远不响的铃;
|
|
143
|
+
* ③ 再挂 `agent/created`/`session/created` 兜底(宿主事件面若换名,至少还有这条能自愈);
|
|
144
|
+
* ④ 启动时同步一次(`apply` 时按当前配置定初始态)。
|
|
145
|
+
*
|
|
146
|
+
* ⚠️ **失败语义**:本面**默认关**且**依赖可选**(`dsh-acp` 是 peer)⇒ 起不来**只记日志**,
|
|
147
|
+
* 绝不抛出(抛出会让整个插件加载失败,而它只是一个可选面)。
|
|
148
|
+
*/
|
|
149
|
+
export declare function registerAcpFace(ctx: Context): void;
|
package/lib/cro.d.ts
CHANGED
|
@@ -45,6 +45,14 @@
|
|
|
45
45
|
*/
|
|
46
46
|
/** 入口文件名(**全写**,所有者令 2026-09-19;不改缩写) */
|
|
47
47
|
export declare const CRO_FILENAME = "continuous-re-occurrence.ts";
|
|
48
|
+
/**
|
|
49
|
+
* 机器绑定文件名(**全写**,与 `CRO_FILENAME` 同风格)。
|
|
50
|
+
*
|
|
51
|
+
* 🔴 **它随 CCC git 走是设计意图,不是缺陷**:
|
|
52
|
+
* 绑定声明必须跟着**轨迹**走,才能在第二台机器上被读到、并被判为"不是我的"。
|
|
53
|
+
* (这正是机器绑定的整个目的 —— 见 `resolveCroHostBinding` 的头注。)
|
|
54
|
+
*/
|
|
55
|
+
export declare const CRO_HOST_FILENAME = "cro-host-binding";
|
|
48
56
|
/** 硬超时(沿用 `biasProvider` 先例的 60s;设计 §5 情形 2) */
|
|
49
57
|
export declare const CRO_TIMEOUT_MS = 60000;
|
|
50
58
|
/**
|
|
@@ -223,6 +231,88 @@ export type CroRunner = (scriptAbs: string, stdinJson: string, timeoutMs: number
|
|
|
223
231
|
export declare function croScriptPath(root: string, dirName: string): string;
|
|
224
232
|
/** CRO 是否启用(**判据 = 文件在不在**;设计 §2.1:无 enabled 字段、无注册表) */
|
|
225
233
|
export declare function isCroEnabled(root: string, dirName: string): boolean;
|
|
234
|
+
/**
|
|
235
|
+
* 机器绑定判定结果(**永不抛**)。
|
|
236
|
+
*
|
|
237
|
+
* 三态而非布尔:**「没绑」与「绑给别人」必须分得开**(见下"为什么三态")。
|
|
238
|
+
*/
|
|
239
|
+
export interface CroHostVerdict {
|
|
240
|
+
/** 这台机器可以跑吗(唯一决定投递与否的字段) */
|
|
241
|
+
allowed: boolean;
|
|
242
|
+
/** 绑定文件里写的机器名(未绑/读不到 ⇒ `null`) */
|
|
243
|
+
bound: string | null;
|
|
244
|
+
/** 本机机器名 */
|
|
245
|
+
self: string;
|
|
246
|
+
/**
|
|
247
|
+
* 机器可读的判定原因(稳定码,供测试与日志断言)。
|
|
248
|
+
* · `bound-to-self` —— 已绑且是本机 ⇒ 跑
|
|
249
|
+
* · `not-bound` —— **没有绑定文件**(或文件是空的)⇒ **不跑**(fail-closed)
|
|
250
|
+
* · `bound-to-other` —— 绑给了别的机器 ⇒ 不跑
|
|
251
|
+
* · `read-error` —— 文件在但读不了 ⇒ 不跑(fail-closed)
|
|
252
|
+
*/
|
|
253
|
+
code: 'bound-to-self' | 'not-bound' | 'bound-to-other' | 'read-error';
|
|
254
|
+
/** 人读说明(进 tick 日志) */
|
|
255
|
+
detail: string;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* 解析机器绑定 —— 「**这条轨迹由哪台机器负责自唤醒**」(所有者令 2026-10-15)。
|
|
259
|
+
*
|
|
260
|
+
* ## 所有者裁定的语义(逐字,D135)
|
|
261
|
+
*
|
|
262
|
+
* 「**CRO 必须指定绑定机器,否则不运行,唤醒器 检查这个**」⇒ 三条同时成立:
|
|
263
|
+
* ① **只许这一台**(白名单,不是"排除这一台");
|
|
264
|
+
* ② **fail-closed** —— **没绑 = 不跑**(不是"没绑 = 随便跑");
|
|
265
|
+
* ③ **检查者在唤醒器**(本函数由 `runCroPhase` 调用,不是程序自己判)。
|
|
266
|
+
*
|
|
267
|
+
* ## 绑定键 = 机器名(所有者 2026-10-15 裁「用 hostname 吧,比较合理」)
|
|
268
|
+
*
|
|
269
|
+
* ⚠️ **已接受的取舍**:本机机器名是 QEMU 机型派生的默认形
|
|
270
|
+
* (`yh-Standard-PC-Q35-ICH9-2009`)⇒ 同法新建的同名用户 VM 会取到同一个名字。
|
|
271
|
+
* 所有者 2026-10-15 明确「**不改名,家里就这几台机器没必要**」⇒ **风险由所有者接受**。
|
|
272
|
+
*
|
|
273
|
+
* ## 🔴 为什么必须是三态,「没绑」不能并进「绑给别人」
|
|
274
|
+
*
|
|
275
|
+
* 2026-10-15 已发生过**同族事故**:`send-now` 失败时报的是「无绑定会话记录」,
|
|
276
|
+
* 而真因是"那条轨迹不可寻址"—— **文案相同、原因不同**,害得排查方向错了一次
|
|
277
|
+
* (S142 §日志 续 129 / D134)。
|
|
278
|
+
* ⇒ 绑定这一层**不许再造同样的坑**:「你没绑」与「你绑给了别人」是**两件不同的事**,
|
|
279
|
+
* 日志必须分开写 —— 否则将来"为什么这条轨迹不跑了"会又一次指错方向。
|
|
280
|
+
*
|
|
281
|
+
* ## 文件形态(刻意的极简)
|
|
282
|
+
*
|
|
283
|
+
* · 位置 = `<轨迹目录>/cro-host-binding`(与 CRO 程序**同级**)
|
|
284
|
+
* · 内容 = **一行机器名**(首尾空白裁掉;只取第一行有效内容)
|
|
285
|
+
* · 不用 JSON —— 它只有一个值,JSON 只会多一种写错的方式
|
|
286
|
+
* · 支持 `#` 注释行与空行(方便人写、方便留痕)
|
|
287
|
+
*
|
|
288
|
+
* 🔴 **它必须随 git 入库**(不是"运行态"):绑定声明要**跟着轨迹走到第二台机器**,
|
|
289
|
+
* 在那里被判成「绑给了别人」而**正确地不跑** —— 那就是本机制在工作。
|
|
290
|
+
* ⇒ ⛔ **不要把它加进 `.gitignore`**(那会让第二台机器读到"没绑"而同样不跑,
|
|
291
|
+
* 表面上结果一样,但**判据从"明确绑给别人"退化成"什么都没说"**,失去可诊断性)。
|
|
292
|
+
*
|
|
293
|
+
* ## 为什么读文件而不是读快照字段
|
|
294
|
+
*
|
|
295
|
+
* 快照(`CroSnapshot`)是喂给**程序**的;而本判定发生在**程序被 spawn 之前**
|
|
296
|
+
* —— 程序还没跑,没有快照可读。⇒ 只能读文件。(这也是"机制属 ACC"的体现:
|
|
297
|
+
* ACC 在调用用户程序前先判定自己该不该调用。)
|
|
298
|
+
*
|
|
299
|
+
* @param root CCC 根
|
|
300
|
+
* @param dirName 轨迹目录名
|
|
301
|
+
* @param selfHost 本机机器名(**注入** ⇒ 可测;生产调用方传 `hostname()`)
|
|
302
|
+
* @returns 判定结果(**永不抛** —— 旁路容忍铁律)
|
|
303
|
+
*/
|
|
304
|
+
export declare function resolveCroHostBinding(root: string, dirName: string, selfHost: string): CroHostVerdict;
|
|
305
|
+
/**
|
|
306
|
+
* 解析绑定文件正文 ⇒ 机器名(取不到 ⇒ `null`)。
|
|
307
|
+
*
|
|
308
|
+
* 规则(刻意宽松:**只认一行,其余都当注释**):
|
|
309
|
+
* · 逐行扫;跳过空行与 `#` 开头的行;
|
|
310
|
+
* · **第一条有效行** = 机器名(裁首尾空白);
|
|
311
|
+
* · 无有效行 ⇒ `null`。
|
|
312
|
+
* @param raw 文件正文
|
|
313
|
+
* @returns 机器名;无有效内容 ⇒ `null`
|
|
314
|
+
*/
|
|
315
|
+
export declare function parseCroHostBinding(raw: string): string | null;
|
|
226
316
|
/**
|
|
227
317
|
* 列出**启用了 CRO 的轨迹目录名**(供调度器每 tick 扫描,设计 §6.1)。
|
|
228
318
|
*
|
package/lib/index.d.ts
CHANGED
|
@@ -174,6 +174,16 @@ export interface Config {
|
|
|
174
174
|
unattendedEnabled?: Volatile<boolean | undefined>;
|
|
175
175
|
/** **diagram 工具总闸**(面板层;部署层同义键 = `diagram.enabled`;缺省关;D104) */
|
|
176
176
|
diagramEnabled?: Volatile<boolean | undefined>;
|
|
177
|
+
/**
|
|
178
|
+
* 🔴 **面 E:官方 ACP 总闸**(面板层;部署层同义键 = `acpFace.enabled`;**缺省关**;2026-10-16)。
|
|
179
|
+
*
|
|
180
|
+
* ⚠️ **键名刻意不叫 `acpEnabled`** —— 那是**旧面 E(3100 自造问答面)**的键,
|
|
181
|
+
* 已随 owner「砍掉外部问答」裁决退役、现被**静默忽略**。复用同名会让"当年那份旧配置"
|
|
182
|
+
* 在新面上复活(判据:**退役的是功能,名字仍须留作"无人读"**,否则退役不彻底)。
|
|
183
|
+
*/
|
|
184
|
+
acpFaceEnabled?: Volatile<boolean | undefined>;
|
|
185
|
+
/** 面 E 端口(面板层;部署层同义键 = `acpFace.port`;缺省 3200) */
|
|
186
|
+
acpFacePort?: Volatile<number | undefined>;
|
|
177
187
|
}
|
|
178
188
|
/** schemastery 的 schema 类型(别名只为可读;**名字取自全局命名空间**,见上方 TS2742 说明) */
|
|
179
189
|
type SchemaOf<S, T = S> = Schemastery<S, T>;
|
|
@@ -230,6 +240,13 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
230
240
|
}>>, Schemastery.ObjectT<NoInfer<{
|
|
231
241
|
enabled: z<boolean, boolean, "defined">;
|
|
232
242
|
}>>, "plain">;
|
|
243
|
+
acpFace: z<Schemastery.ObjectS<NoInfer<{
|
|
244
|
+
enabled: z<boolean, boolean, "defined">;
|
|
245
|
+
port: z<number, number, "defined">;
|
|
246
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
247
|
+
enabled: z<boolean, boolean, "defined">;
|
|
248
|
+
port: z<number, number, "defined">;
|
|
249
|
+
}>>, "plain">;
|
|
233
250
|
humanChannel: z<Schemastery.ObjectS<NoInfer<{
|
|
234
251
|
accounts: z<HumanChannelAccountConfig[], HumanChannelAccountConfig[], "defined">;
|
|
235
252
|
subscriptions: z<HumanChannelSubscriptionConfig[], HumanChannelSubscriptionConfig[], "defined">;
|
|
@@ -264,6 +281,8 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
264
281
|
croEnabled: z<boolean, boolean, "volatile">;
|
|
265
282
|
unattendedEnabled: z<boolean, boolean, "volatile">;
|
|
266
283
|
diagramEnabled: z<boolean, boolean, "volatile">;
|
|
284
|
+
acpFaceEnabled: z<boolean, boolean, "volatile">;
|
|
285
|
+
acpFacePort: z<number, number, "volatile">;
|
|
267
286
|
}>>, Schemastery.ObjectT<NoInfer<{
|
|
268
287
|
serenityConfigPaths: z<string[], string[], "defined">;
|
|
269
288
|
tools: z<boolean, boolean, "defined">;
|
|
@@ -313,6 +332,13 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
313
332
|
}>>, Schemastery.ObjectT<NoInfer<{
|
|
314
333
|
enabled: z<boolean, boolean, "defined">;
|
|
315
334
|
}>>, "plain">;
|
|
335
|
+
acpFace: z<Schemastery.ObjectS<NoInfer<{
|
|
336
|
+
enabled: z<boolean, boolean, "defined">;
|
|
337
|
+
port: z<number, number, "defined">;
|
|
338
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
339
|
+
enabled: z<boolean, boolean, "defined">;
|
|
340
|
+
port: z<number, number, "defined">;
|
|
341
|
+
}>>, "plain">;
|
|
316
342
|
humanChannel: z<Schemastery.ObjectS<NoInfer<{
|
|
317
343
|
accounts: z<HumanChannelAccountConfig[], HumanChannelAccountConfig[], "defined">;
|
|
318
344
|
subscriptions: z<HumanChannelSubscriptionConfig[], HumanChannelSubscriptionConfig[], "defined">;
|
|
@@ -347,6 +373,8 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
347
373
|
croEnabled: z<boolean, boolean, "volatile">;
|
|
348
374
|
unattendedEnabled: z<boolean, boolean, "volatile">;
|
|
349
375
|
diagramEnabled: z<boolean, boolean, "volatile">;
|
|
376
|
+
acpFaceEnabled: z<boolean, boolean, "volatile">;
|
|
377
|
+
acpFacePort: z<number, number, "volatile">;
|
|
350
378
|
}>>, "plain">;
|
|
351
379
|
export declare function apply(ctx: Context, config: Config): void;
|
|
352
380
|
export {};
|