pactive 0.0.1-alpha.1 → 0.0.2-alpha.1
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/.opcteam-plugin/plugin.json +4 -4
- package/BREAKING-CHANGES.md +23 -7
- package/README.md +50 -48
- package/agents/{acti-observer.md → pactive-observer.md} +4 -4
- package/bin/{acti.mjs → pactive.mjs} +40 -40
- package/commands/pactive.md +46 -0
- package/dsh/cordis.patch.yml +4 -4
- package/dsh/index.js +24 -24
- package/hooks/post-tool-use.mjs +1 -1
- package/hooks/session-end.mjs +1 -1
- package/hooks/session-start.mjs +1 -1
- package/hooks/stop.mjs +2 -2
- package/hooks/user-prompt-submit.mjs +2 -2
- package/mcp/server.mjs +4 -4
- package/mcp/tools/_core.mjs +6 -6
- package/mcp/tools/definitions.mjs +12 -12
- package/package.json +4 -5
- package/pi/extension.ts +35 -35
- package/{skills/acti → pi/skills/pactive}/SKILL.md +13 -13
- package/{pi/skills/acti → skills/pactive}/SKILL.md +13 -13
- package/src/core/config.mjs +2 -2
- package/src/core/errors.mjs +1 -1
- package/src/core/identity.mjs +2 -2
- package/src/core/lineage.mjs +2 -2
- package/src/core/lock.mjs +2 -2
- package/src/core/paths.mjs +6 -6
- package/src/core/scheduler.mjs +5 -5
- package/src/core/state.mjs +7 -7
- package/src/hosts/dsh/index.mjs +12 -12
- package/src/hosts/errors.mjs +5 -5
- package/src/hosts/pi/index.mjs +1 -1
- package/src/hosts/shared/index.mjs +3 -3
- package/commands/acti.md +0 -46
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
2
|
+
"name": "pactive",
|
|
3
|
+
"version": "0.0.2-alpha.1",
|
|
4
|
+
"description": "Pactive 主动机制:空闲判定、定时任务与跨会话消息(hooks + MCP)。",
|
|
5
5
|
"author": { "name": "pk09" },
|
|
6
6
|
"mcpServers": {
|
|
7
|
-
"
|
|
7
|
+
"pactive": {
|
|
8
8
|
"command": "node",
|
|
9
9
|
"args": ["${OPCTEAM_PLUGIN_ROOT}/mcp/server.mjs"]
|
|
10
10
|
}
|
package/BREAKING-CHANGES.md
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# Breaking Changes
|
|
2
2
|
|
|
3
|
-
本文件记录
|
|
3
|
+
本文件记录 Pactive 的**破坏性变更**。
|
|
4
4
|
|
|
5
|
-
## 协议版本:`
|
|
5
|
+
## 协议版本:`pactive/1`
|
|
6
6
|
|
|
7
|
-
事件协议(ledger 事件、注入载荷、回执)的版本标识为 `
|
|
7
|
+
事件协议(ledger 事件、注入载荷、回执)的版本标识为 `pactive/1`。
|
|
8
8
|
|
|
9
9
|
**规则:只加不改。**
|
|
10
10
|
|
|
11
11
|
- **可以**:新增事件类型、新增可选字段、新增枚举值(且旧消费方对未知值必须安全降级)。
|
|
12
12
|
- **不可以**:重命名 / 删除已发布的字段或事件名、改变字段语义、收紧已发布字段的可接受取值范围、
|
|
13
13
|
改变已发布字段的必填性。
|
|
14
|
-
- 若某次变更无法在「只加不改」下表达,则必须**提升协议版本**(如 `
|
|
14
|
+
- 若某次变更无法在「只加不改」下表达,则必须**提升协议版本**(如 `pactive/2`),
|
|
15
15
|
并在本文件顶部新增一节说明迁移路径;旧版本**继续可用**,不删除、不 unpublish。
|
|
16
16
|
|
|
17
17
|
## 变更记录格式
|
|
@@ -29,11 +29,27 @@
|
|
|
29
29
|
|
|
30
30
|
## 未发布
|
|
31
31
|
|
|
32
|
-
当前版本 `0.0.
|
|
32
|
+
当前版本 `0.0.2-alpha.1` 尚未发布,暂无已发布的破坏性变更记录。
|
|
33
|
+
|
|
34
|
+
## 0.0.2-alpha.1 — 2026-11-05(尚未发布)
|
|
35
|
+
|
|
36
|
+
### 破坏性
|
|
37
|
+
|
|
38
|
+
- **产品/命令全量改名:`acti` → `pactive`**:旧行为 = 命令 `acti`、包内 CLI `bin/acti.mjs`、MCP server/工具名
|
|
39
|
+
`acti` / `acti_status` / `acti_schedule` / `acti_tell` / `acti_history`、Pi 命令 `/acti`、环境变量 `ACTI_*`、
|
|
40
|
+
默认状态根 `~/.acti`、协议串 `acti/1`;新行为 = 命令 `pactive`(`bin` 只剩 `{"pactive": "bin/pactive.mjs"}`)、
|
|
41
|
+
MCP `pactive` / `pactive_status` 等、Pi 命令 `/pactive`、环境变量 `PACTIVE_*`、默认状态根 `~/.pactive`、
|
|
42
|
+
协议串 `pactive/1`。
|
|
43
|
+
- 迁移:把脚本/集成里的 `acti` 命令换成 `pactive`,`ACTI_*` 换成 `PACTIVE_*`,`~/.acti` 状态根改到 `~/.pactive`
|
|
44
|
+
(或继续用 `PACTIVE_STATE_DIR` 显式指定旧目录以保留原状态);MCP 工具名与 `/acti` 命令同步改名;协议串按 `pactive/1` 解析。
|
|
45
|
+
- **旧命令不保留兼容别名**:`acti` 命令与 `ACTI_*` 环境变量**不再提供**(不注册别名、不读旧变量)。
|
|
46
|
+
旧版仍在 npm `@pk09/acti`(历史版本名),可继续安装使用,但不再随本包演进。
|
|
47
|
+
- **状态根默认值变化**:默认由 `~/.acti` 改为 `~/.pactive`。不迁移旧目录内容;需要沿用旧状态时显式设置
|
|
48
|
+
`PACTIVE_STATE_DIR=<旧目录>`。
|
|
33
49
|
|
|
34
50
|
## 0.0.1-alpha.1 — 2026-11-05(尚未发布)
|
|
35
51
|
|
|
36
52
|
### 破坏性
|
|
37
53
|
|
|
38
|
-
- 无。这是 `
|
|
39
|
-
`docs/team/task-cards/ACTI-CONTRACT.md
|
|
54
|
+
- 无。这是 `pactive/1` 的首个版本,协议形状由
|
|
55
|
+
`docs/team/task-cards/ACTI-CONTRACT.md`(历史文件名)冻结;冻结之后只增不改。
|
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Pactive
|
|
2
2
|
|
|
3
3
|
跨宿主的**主动能力运行时**:在宿主(Pi / OPCteam / DSH)之外统一判定「该不该主动说话」,
|
|
4
4
|
把触发、投递、回应、结账写进一条 append-only 事件账本,并提供 CLI / doctor / 定时任务 / 跨会话消息。
|
|
5
5
|
|
|
6
|
-
> **状态:v0.0.
|
|
6
|
+
> **状态:v0.0.2-alpha.1(预发布)。** 本文只描述**契约层面**的接口。
|
|
7
7
|
> 每一项能力后都标注了**当前实际验证到哪一步**——绝大多数条目当前为「未验证」。
|
|
8
8
|
> 本文档不构成「已支持」的声明;请以「宿主兼容矩阵」与「未验证项」两节为准。
|
|
9
9
|
|
|
@@ -13,16 +13,16 @@
|
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
15
|
# 从 npm(alpha 标签;首次发布前该命令会 404)
|
|
16
|
-
npm i -g pactive # 命令:pactive
|
|
16
|
+
npm i -g pactive # 命令:pactive(旧命令已退役,见 BREAKING-CHANGES.md)
|
|
17
17
|
|
|
18
18
|
# 从本地 tarball
|
|
19
19
|
npm pack
|
|
20
|
-
npm i -g --prefix /tmp/
|
|
20
|
+
npm i -g --prefix /tmp/pactive-prefix ./pactive-0.0.2-alpha.1.tgz
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
运行环境:**Node.js ≥ 22.19.0**(`engines.node`)。
|
|
24
24
|
|
|
25
|
-
安装后提供单个可执行文件 `
|
|
25
|
+
安装后提供单个可执行文件 `pactive`。本包**不含**源码仓库中的测试目录(`files` 白名单不含 `tests/`),
|
|
26
26
|
测试与门禁脚本仅存在于源码仓库。
|
|
27
27
|
|
|
28
28
|
包元数据见 `package.json`:除设计 §10 列出的字段外,另加了 `pi`(Pi 宿主加载扩展/技能所需)、
|
|
@@ -32,39 +32,39 @@ npm i -g --prefix /tmp/acti-prefix ./pactive-0.0.1-alpha.1.tgz
|
|
|
32
32
|
|
|
33
33
|
## 2. 用法
|
|
34
34
|
|
|
35
|
-
### 2.1 启动宿主(带
|
|
35
|
+
### 2.1 启动宿主(带 Pactive 注入参数)
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
pactive pi -p "继续处理积压任务" # pi --extension <pkg>/pi/extension.ts --skill <pkg>/pi/skills/pactive
|
|
39
|
+
pactive opcteam -p "继续处理积压任务" # opcteam --plugin-dir <pkg>
|
|
40
|
+
pactive dsh # dsh --profile pactive(只写 pactive 专用 profile)
|
|
41
41
|
```
|
|
42
42
|
- 注入参数在前,用户参数原样追加。
|
|
43
43
|
- Pi 宿主通过包内 `pi` 字段声明扩展与技能(`pi.extensions` → `pi/extension.ts`,
|
|
44
|
-
`pi.skills` → `pi/skills/
|
|
44
|
+
`pi.skills` → `pi/skills/pactive`);`pi install pactive` 依赖该字段。
|
|
45
45
|
- 宿主退出码 / 信号原样透传(信号 → 128+n)。
|
|
46
|
-
- **stdout 纯净**:
|
|
46
|
+
- **stdout 纯净**:Pactive 自身的回执、诊断、进度一律走 stderr,
|
|
47
47
|
stdout 只承载被启动宿主的输出,以及 `--print-plan` / `doctor --json` 的机器可读结果。
|
|
48
48
|
|
|
49
49
|
### 2.2 其它子命令
|
|
50
50
|
|
|
51
51
|
| 命令 | 行为 | 退出码 |
|
|
52
52
|
|---|---|---|
|
|
53
|
-
| `
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
59
|
-
| 宿主二进制缺失 | 提示安装 + `
|
|
53
|
+
| `pactive --version` / `-v` | 打印版本号 | 0 |
|
|
54
|
+
| `pactive --help` / `-h` | 列出三个宿主、子命令、环境变量 | 0 |
|
|
55
|
+
| `pactive --print-plan <host>` | 打印注入计划 JSON(零写盘) | 0(未知宿主 2) |
|
|
56
|
+
| `pactive doctor [--json]` | 宿主 / 版本 / 注入模式 / 状态根 / 权限 / 残留 | 0(有 FAIL 段则 1) |
|
|
57
|
+
| `pactive install <host>` / `pactive uninstall <host>` | 幂等安装 / 卸载 | 0(有残留则 1) |
|
|
58
|
+
| `pactive <未知宿主>` | 列出 pi / opcteam / dsh 到 stderr | 2 |
|
|
59
|
+
| 宿主二进制缺失 | 提示安装 + `PACTIVE_<HOST>_BIN` 覆盖 | 127 |
|
|
60
60
|
|
|
61
61
|
### 2.3 环境变量
|
|
62
62
|
|
|
63
63
|
| 变量 | 作用 |
|
|
64
64
|
|---|---|
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
65
|
+
| `PACTIVE_STATE_DIR` | 状态根覆盖(默认按宿主约定解析) |
|
|
66
|
+
| `PACTIVE_HOME` | 包根覆盖(测试用) |
|
|
67
|
+
| `PACTIVE_PI_BIN` / `PACTIVE_OPCTEAM_BIN` / `PACTIVE_DSH_BIN` | 宿主二进制路径覆盖 |
|
|
68
68
|
|
|
69
69
|
---
|
|
70
70
|
|
|
@@ -74,12 +74,12 @@ acti dsh # dsh --profile acti(只写 acti 专用 pr
|
|
|
74
74
|
|
|
75
75
|
| 宿主 | 本机版本 | 注入方式 | 当前验证到哪一步 |
|
|
76
76
|
|---|---|---|---|
|
|
77
|
-
| Pi | 1.4.0(`pi --version` 实测) | `pi --extension <pkg>/pi/extension.ts --skill <pkg>/pi/skills/
|
|
78
|
-
| OPCteam | 1.1.6-test(`opcteam --version` 实测) | `opcteam --plugin-dir <pkg>`(包根即插件根) | **已验证**:`opcteam plugin validate <pkg>` → ✔ Validation passed
|
|
79
|
-
| DSH | 0.2.1-rc.5(`dsh --version` 实测) | `dsh plugin --profile
|
|
77
|
+
| Pi | 1.4.0(`pi --version` 实测) | `pi --extension <pkg>/pi/extension.ts --skill <pkg>/pi/skills/pactive` | **已验证**:扩展真实加载 + `session_start` 触发 + `/pactive status` 在真实 Pi 里输出状态行(`pactive pi -p "/pactive status"` → `[pactive] ... idle=false reason=disabled`,exit 0)。**未验证**:TUI 交互模式下的命令可见性、`--skill` 技能是否被检索到 |
|
|
78
|
+
| OPCteam | 1.1.6-test(`opcteam --version` 实测) | `opcteam --plugin-dir <pkg>`(包根即插件根) | **已验证**:`opcteam plugin validate <pkg>` → ✔ Validation passed(manifest `name=pactive`、`mcpServers.pactive`)。**未验证**:本机 opcteam 未登录(`pactive opcteam -p …` → `Not logged in · Please run /login`),改名后的 `/pactive:pactive` 命令识别、`mcp__plugin_pactive_pactive__pactive_status` 注册与工具执行均**未**在真实会话复验;改名前的同一链路曾在登录会话中验证(旧命令面)。 |
|
|
79
|
+
| DSH | 0.2.1-rc.5(`dsh --version` 实测) | `dsh plugin --profile pactive add <pkg>` → `dsh --profile pactive` | **已验证**:`pactive install dsh` 写入专用 profile(`profiles/pactive/package.json` 的 `dsh.profile.bundles` 含 `pactive`),`dsh --profile pactive --dump-config` 输出里 `grep -c '^# == pactive'` = 1(段内 `id: pactive` / `name: pactive/dsh`);`pactive uninstall dsh` 后 profile 目录被删净。**未验证**:真实 GUI / TUI 会话里工具与命令的可用性 |
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
端到端冒烟现状:Pi 与 DSH 两条已实测(见上表),OPCteam 因本机未登录**未完成**。
|
|
82
|
+
因此本文仍不声称任何宿主「已支持」。
|
|
83
83
|
|
|
84
84
|
ACP 通道(第四个 adapter)**本期不实现**,仅留接口 stub 与文档口径,不在 CLI 表面内。
|
|
85
85
|
|
|
@@ -87,14 +87,14 @@ ACP 通道(第四个 adapter)**本期不实现**,仅留接口 stub 与文
|
|
|
87
87
|
|
|
88
88
|
## 4. 默认不打扰
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
Pactive 的默认状态是**不主动说话**:
|
|
91
91
|
|
|
92
92
|
- `idle.enabled = false`(空闲触发默认关闭)。引擎在、开关关。
|
|
93
93
|
- 打开 idle 必须走**人工闸门**:只有 CLI 带 `--confirm` 才能改;
|
|
94
94
|
MCP / 模型调用只能得到 `need_human_confirmation`,配置不变。
|
|
95
95
|
- 空闲判定是四要素合取,缺一不触发:控制权已交回(Stop 后)、当前不忙、
|
|
96
96
|
超过空闲阈值、四道护栏(静默时段 / 每小时上限 / 最小间隔 / 冷却)全部放行。
|
|
97
|
-
-
|
|
97
|
+
- Pactive 自己的注入**不刷新**活动基线;同一活动纪元最多触发一次。
|
|
98
98
|
- 动作集默认只允许 `prompt`;`agent-inline` / `agent-process` 默认关闭。
|
|
99
99
|
|
|
100
100
|
---
|
|
@@ -106,9 +106,9 @@ Acti 的默认状态是**不主动说话**:
|
|
|
106
106
|
- **不联网、不自更新**:启动与诊断不发起网络请求,包内不引入自动更新机制。
|
|
107
107
|
- **权限**:状态目录 0700、状态文件 0600;权限不合要求时显式报错,不静默降级。
|
|
108
108
|
- **默认零持久化**:Pi / OPCteam 默认只在会话内注入,不写用户 settings;
|
|
109
|
-
DSH 只写专用 `
|
|
110
|
-
- **可卸载**:`
|
|
111
|
-
- 发行物含 Pi 扩展源码(`pi/extension.ts`),使用条款见包内 `LICENSE
|
|
109
|
+
DSH 只写专用 `pactive` profile,**不改**用户默认 profile。
|
|
110
|
+
- **可卸载**:`pactive uninstall <host>` 幂等,残留可枚举,残留非 0 即失败退出。
|
|
111
|
+
- 发行物含 Pi 扩展源码(`pi/extension.ts`),使用条款见包内 `LICENSE`(专有许可)。
|
|
112
112
|
|
|
113
113
|
---
|
|
114
114
|
|
|
@@ -118,24 +118,26 @@ Acti 的默认状态是**不主动说话**:
|
|
|
118
118
|
|
|
119
119
|
1. Pi 1.4.0:扩展加载/事件/命令已验证;**TUI 交互模式**下命令可见性与 skill 检索未验证。
|
|
120
120
|
2. DSH Cordis bundle 在真实 GUI / 会话下的行为 —— 只计划验到 `--dump-config` 与工具注册。
|
|
121
|
-
3. OPCteam
|
|
122
|
-
4.
|
|
123
|
-
5. npm
|
|
124
|
-
6. npm 发布与回滚链路(`npm publish --tag alpha` / `npm view
|
|
121
|
+
3. OPCteam:改名后只验证到 `plugin validate`;**命令发现 / MCP 注册 / 人工批准后工具真正执行 / hooks 实战效果**均未在真实会话复验(本机未登录)。
|
|
122
|
+
4. 三宿主端到端冒烟:Pi(`pactive pi -p "/pactive status"` → `[pactive] idle=false reason=disabled`,exit 0)与 DSH(`install` → `--dump-config` 有 `# == pactive` 段 → `uninstall` 零残留)**已实测**;OPCteam 受阻于未登录,**未完成**。
|
|
123
|
+
5. npm 安装链:**已实测**(`npm pack` → `npm i -g --prefix <prefix> ./pactive-0.0.2-alpha.1.tgz` → `<prefix>/bin/pactive --version` = `0.0.2-alpha.1` → `<prefix>/bin/pactive doctor` = OK;同一 PATH 下旧命令 `acti` 已 `command not found`,exit 127)。**未验证**:真实 npm registry 安装(包尚未发布)。
|
|
124
|
+
6. npm 发布与回滚链路(`npm publish --tag alpha` / `npm view pactive version` / `npm deprecate`)。
|
|
125
125
|
7. Windows(Node spawn / 路径差异)—— 未在任何 Windows 机器验证。
|
|
126
126
|
8. ACP 通道(I51–I56)—— 全部未实现、未探针。
|
|
127
127
|
9. 空闲唤醒在 headless 下能否产生第二回合(B12 边界)。
|
|
128
128
|
10. 卸载残留枚举为 0 的实测(`装两次 → 卸两次 → 残留 0`)。
|
|
129
|
-
11. 安全项:假 key 不落地、目录/文件权限、子进程 env 过滤 ——
|
|
129
|
+
11. 安全项:假 key 不落地、目录/文件权限、子进程 env 过滤 —— 用例已落盘于 `tests/security/`,但「子进程 env 过滤」无可测实现(用例 SKIP),该项视为未验证。
|
|
130
130
|
|
|
131
131
|
---
|
|
132
132
|
|
|
133
133
|
## 7. 协议与破坏性变更
|
|
134
134
|
|
|
135
|
-
- 事件协议版本:`
|
|
135
|
+
- 事件协议版本:`pactive/1`。**只加不改**——已发布的字段不重命名、不删除、不改语义。
|
|
136
|
+
- **改名(0.0.2-alpha.1 起)**:命令 = `pactive`;旧命令(历史版本名 `@pk09/acti` 提供)与 `ACTI_*` 环境变量**不再提供**,
|
|
137
|
+
状态根默认值改为 `~/.pactive`。详见 [`BREAKING-CHANGES.md`](./BREAKING-CHANGES.md)。
|
|
136
138
|
- 破坏性变更记录见 [`BREAKING-CHANGES.md`](./BREAKING-CHANGES.md)。
|
|
137
139
|
- 内部 API(`src/core` 导出名、宿主 adapter 签名)以
|
|
138
|
-
`docs/team/task-cards/ACTI-CONTRACT.md`
|
|
140
|
+
`docs/team/task-cards/ACTI-CONTRACT.md` 为冻结契约(任务卡文件名保持历史原名),改名即破坏并行。
|
|
139
141
|
|
|
140
142
|
---
|
|
141
143
|
|
|
@@ -143,15 +145,15 @@ Acti 的默认状态是**不主动说话**:
|
|
|
143
145
|
|
|
144
146
|
```bash
|
|
145
147
|
# 独立性门禁(依赖 / 源码 import / npm pack 产物 三层扫旧包名)
|
|
146
|
-
bash scripts/quality/
|
|
147
|
-
bash scripts/quality/
|
|
148
|
+
bash scripts/quality/pactive-independence-gate.sh
|
|
149
|
+
bash scripts/quality/pactive-independence-gate.sh --self-test # 自测:造命中 → 断言红 → 清理 → 绿
|
|
148
150
|
|
|
149
151
|
# 聚合门禁(S1 core / S2 cli+hosts / S3 pack 单测+白名单+入口存在性 / S4 独立性 / S5 security / S6 文档机检+自测+数字对齐 / S7 结论行)
|
|
150
|
-
bash scripts/quality/
|
|
151
|
-
bash scripts/quality/
|
|
152
|
+
bash scripts/quality/pactive-gate.sh
|
|
153
|
+
bash scripts/quality/pactive-gate.sh S3 # 单段可跑
|
|
152
154
|
```
|
|
153
155
|
|
|
154
|
-
独立性的含义:
|
|
156
|
+
独立性的含义:Pactive **不依赖**旧的 `pi-proactive` / `opcteam-proactive` / `dsh-proactive` / `mcp-proactive` 包,
|
|
155
157
|
三层机检保证这一点(依赖声明 / 源码 import / 打包产物文件名)。
|
|
156
158
|
|
|
157
159
|
## 9. 本仓库目录(发行包 = 仓库根)
|
|
@@ -160,12 +162,12 @@ bash scripts/quality/acti-gate.sh S3 # 单段可跑
|
|
|
160
162
|
|
|
161
163
|
| 路径 | 是什么 | 进 npm 包 |
|
|
162
164
|
|---|---|---|
|
|
163
|
-
| `bin/ src/ pi/ hooks/ mcp/ dsh/ commands/ agents/ skills/ .opcteam-plugin/` | **
|
|
165
|
+
| `bin/ src/ pi/ hooks/ mcp/ dsh/ commands/ agents/ skills/ .opcteam-plugin/` | **Pactive 本体**(本新产品) | ✅ |
|
|
164
166
|
| `tests/` | 测试(core/cli/hosts/pack) | ❌ |
|
|
165
|
-
| `scripts/quality/
|
|
166
|
-
| `docs/
|
|
167
|
+
| `scripts/quality/pactive-*.sh` `scripts/quality/pactive-proactive-probe.mjs` | 门禁与验收探针 | ❌ |
|
|
168
|
+
| `docs/pactive/` | 需求与设计真源(不变量 I1–I56、方案、功能清单、可视化设计、详细设计) | ❌ |
|
|
167
169
|
| `docs/project/` `docs/team/` `docs/review/` | 过程资产(里程碑/排期/回执/任务卡/评审) | ❌ |
|
|
168
|
-
| `pi-proactive/ dsh-proactive/ opcteam-proactive/ mcp-proactive/` | **旧四个包**(仅行为参考,
|
|
170
|
+
| `pi-proactive/ dsh-proactive/ opcteam-proactive/ mcp-proactive/` | **旧四个包**(仅行为参考,Pactive 不依赖) | ❌ |
|
|
169
171
|
| `dist/` `marketplace/` `runtime/` | 历史交付制品与分发入口 | ❌ |
|
|
170
172
|
|
|
171
|
-
判据:`npm pack --dry-run --json | grep -c '"path"'` →
|
|
173
|
+
判据:`npm pack --dry-run --json | grep -c '"path"'` → 41(当前实测),且不含 `tests/`,由 `scripts/quality/pactive-gate.sh` S3 段机检。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
3
|
-
description: 观察
|
|
2
|
+
name: pactive-observer
|
|
3
|
+
description: 观察 pactive 的空闲判定与主动触发是否真的发生,只报可核验的事实
|
|
4
4
|
tools:
|
|
5
5
|
- Read
|
|
6
6
|
- Grep
|
|
@@ -10,11 +10,11 @@ permissionMode: default
|
|
|
10
10
|
maxTurns: 8
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
你是
|
|
13
|
+
你是 pactive 的观察 agent:负责回答「主动机制到底有没有按预期工作」。
|
|
14
14
|
|
|
15
15
|
## 任务
|
|
16
16
|
|
|
17
|
-
1. 读 `
|
|
17
|
+
1. 读 `pactive_status`(或 `pactive history`)拿到**当前**的空闲判定、`reason`、护栏结论与事件账本尾部。
|
|
18
18
|
2. 对照用户的期望回答:**触发了吗?为什么没触发?**
|
|
19
19
|
3. 只报告**新事实**:具体的事件、具体的时间戳、具体的 `reason`,不要重复用户已知内容。
|
|
20
20
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
2
|
+
// Pactive CLI — contract §3 / design §6.
|
|
3
3
|
//
|
|
4
4
|
// Output discipline (design §6.3): machine-readable results (--print-plan, doctor --json,
|
|
5
|
-
// --version) go to stdout; every human/diagnostic
|
|
5
|
+
// --version) go to stdout; every human/diagnostic Pactive text goes to stderr, so the stdout of a
|
|
6
6
|
// spawned host is never polluted.
|
|
7
7
|
import { spawn, spawnSync } from 'node:child_process';
|
|
8
8
|
import { existsSync, readdirSync, readFileSync, realpathSync, rmSync, statSync } from 'node:fs';
|
|
@@ -10,47 +10,47 @@ import os from 'node:os';
|
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import { PactiveError } from '../src/hosts/errors.mjs';
|
|
14
14
|
|
|
15
15
|
const HOSTS = ['pi', 'opcteam', 'dsh'];
|
|
16
16
|
const CLI_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
17
17
|
const PKG_ROOT = path.resolve(CLI_DIR, '..');
|
|
18
|
-
const FALLBACK_VERSION = '0.0.
|
|
18
|
+
const FALLBACK_VERSION = '0.0.2-alpha.1';
|
|
19
19
|
const STALE_LOCK_MS = 30 * 60 * 1000;
|
|
20
20
|
|
|
21
21
|
function usage(io) {
|
|
22
|
-
io.write(`
|
|
22
|
+
io.write(`pactive — proactive companion for Pi / OPCteam / DSH
|
|
23
23
|
|
|
24
24
|
Usage:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
pactive <host> [args...] run a host with pactive injected (stdio inherited)
|
|
26
|
+
pactive --print-plan <host> print the launch plan as JSON (no side effects)
|
|
27
|
+
pactive doctor [--json] diagnose hosts, state root and permissions (read-only)
|
|
28
|
+
pactive install <host> register pactive with a host (idempotent)
|
|
29
|
+
pactive uninstall <host> unregister pactive and remove pactive state residue
|
|
30
|
+
pactive --version | -v print the pactive version
|
|
31
|
+
pactive --help | -h show this help
|
|
32
32
|
|
|
33
33
|
Hosts: ${HOSTS.join(', ')}
|
|
34
34
|
|
|
35
35
|
Environment:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
PACTIVE_HOME package root override (default: this package)
|
|
37
|
+
PACTIVE_STATE_DIR pactive state root override
|
|
38
|
+
PACTIVE_PI_BIN override the pi executable
|
|
39
|
+
PACTIVE_OPCTEAM_BIN override the opcteam executable
|
|
40
|
+
PACTIVE_DSH_BIN override the dsh executable
|
|
41
41
|
`);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
function unknownHost(host, io) {
|
|
45
|
-
io.write(`
|
|
46
|
-
io.write(`
|
|
45
|
+
io.write(`pactive: unknown host '${host}'\n`);
|
|
46
|
+
io.write(`pactive: known hosts: ${HOSTS.join(', ')}\n`);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
function resolvePkgRoot(env) {
|
|
50
|
-
return env.
|
|
50
|
+
return env.PACTIVE_HOME ? path.resolve(env.PACTIVE_HOME) : PKG_ROOT;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
function
|
|
53
|
+
function pactiveVersion() {
|
|
54
54
|
try {
|
|
55
55
|
const pkg = JSON.parse(readFileSync(path.join(PKG_ROOT, 'package.json'), 'utf8'));
|
|
56
56
|
if (typeof pkg.version === 'string') return pkg.version;
|
|
@@ -64,26 +64,26 @@ async function loadAdapter(host) {
|
|
|
64
64
|
return import(new URL(`../src/hosts/${host}/index.mjs`, import.meta.url));
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
/** src/core is built by the
|
|
67
|
+
/** src/core is built by the pactive-core worker; until then the CLI degrades instead of faking it. */
|
|
68
68
|
async function loadCore() {
|
|
69
69
|
try {
|
|
70
70
|
return await import(new URL('../src/core/index.mjs', import.meta.url));
|
|
71
71
|
} catch (err) {
|
|
72
|
-
throw new
|
|
72
|
+
throw new PactiveError('PACTIVE core not built yet', { code: 'CORE_MISSING', exitCode: 1, cause: err });
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
async function resolveStateRoot(env, warnings = []) {
|
|
77
|
-
if (env.
|
|
77
|
+
if (env.PACTIVE_STATE_DIR) return path.resolve(env.PACTIVE_STATE_DIR);
|
|
78
78
|
try {
|
|
79
79
|
const core = await loadCore();
|
|
80
80
|
const paths = core.resolvePaths('pi', env);
|
|
81
81
|
if (paths?.root) return paths.root;
|
|
82
|
-
warnings.push('
|
|
82
|
+
warnings.push('PACTIVE core returned no state root — using the default location');
|
|
83
83
|
} catch (err) {
|
|
84
84
|
warnings.push(`${err.message} — using the default state root`);
|
|
85
85
|
}
|
|
86
|
-
return path.join(os.homedir(), '.
|
|
86
|
+
return path.join(os.homedir(), '.pactive');
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
function modeOf(p) {
|
|
@@ -198,7 +198,7 @@ async function doctorReport(env) {
|
|
|
198
198
|
}
|
|
199
199
|
} catch (err) {
|
|
200
200
|
if (err.code !== 'HOST_MISSING') throw err;
|
|
201
|
-
warnings.push(`host ${host}: not installed (set
|
|
201
|
+
warnings.push(`host ${host}: not installed (set PACTIVE_${host.toUpperCase()}_BIN to override)`);
|
|
202
202
|
}
|
|
203
203
|
hosts.push({ host, present, version, mode: mod.MODE, compatible });
|
|
204
204
|
}
|
|
@@ -245,7 +245,7 @@ function doctorCmd(args, env, io) {
|
|
|
245
245
|
if (asJson) {
|
|
246
246
|
io.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
247
247
|
} else {
|
|
248
|
-
io.stderr.write(`
|
|
248
|
+
io.stderr.write(`pactive doctor: ${report.ok ? 'OK' : 'FAIL'}\n`);
|
|
249
249
|
io.stderr.write(`state root: ${report.stateRoot} (dir ${report.perm.dir}, file ${report.perm.file})\n`);
|
|
250
250
|
for (const h of report.hosts) {
|
|
251
251
|
io.stderr.write(
|
|
@@ -317,7 +317,7 @@ function installCmd(args, env, io, action) {
|
|
|
317
317
|
if (typeof mod.cleanup === 'function') {
|
|
318
318
|
try { mod.cleanup({ pkgRoot, env }); } catch { /* residue 清理失败由下方 residue 检查兜底 */ }
|
|
319
319
|
}
|
|
320
|
-
io.stderr.write(`
|
|
320
|
+
io.stderr.write(`pactive: ${host} not installed — nothing to uninstall\n`);
|
|
321
321
|
return 0;
|
|
322
322
|
}
|
|
323
323
|
}
|
|
@@ -327,31 +327,31 @@ function installCmd(args, env, io, action) {
|
|
|
327
327
|
if (res.status !== 0) {
|
|
328
328
|
const stillRegistered = verifyRegistered(mod, exec, pkgRoot);
|
|
329
329
|
if (action === 'install' ? !stillRegistered : stillRegistered) {
|
|
330
|
-
io.stderr.write(`
|
|
330
|
+
io.stderr.write(`pactive: ${action} ${host} failed (exit ${res.status})\n`);
|
|
331
331
|
if (res.stderr.trim()) io.stderr.write(`${res.stderr.trim()}\n`);
|
|
332
332
|
return 1;
|
|
333
333
|
}
|
|
334
334
|
}
|
|
335
335
|
|
|
336
336
|
if (action === 'uninstall') {
|
|
337
|
-
// Host-level residue: adapters may own resources
|
|
337
|
+
// Host-level residue: adapters may own resources Pactive created (e.g. the DSH profile dir).
|
|
338
338
|
if (typeof mod.cleanup === 'function') {
|
|
339
339
|
try {
|
|
340
340
|
const cleaned = mod.cleanup({ pkgRoot, env });
|
|
341
|
-
if (cleaned && cleaned.removed) io.stderr.write(`
|
|
341
|
+
if (cleaned && cleaned.removed) io.stderr.write(`pactive: removed ${cleaned.path}\n`);
|
|
342
342
|
} catch (err) {
|
|
343
|
-
io.stderr.write(`
|
|
343
|
+
io.stderr.write(`pactive: cleanup warning: ${err?.message ?? err}\n`);
|
|
344
344
|
}
|
|
345
345
|
}
|
|
346
346
|
const stateRoot = await resolveStateRoot(env, []);
|
|
347
347
|
const residue = removeStateResidue(stateRoot);
|
|
348
348
|
if (residue.length > 0) {
|
|
349
|
-
io.stderr.write(`
|
|
349
|
+
io.stderr.write(`pactive: residue left in ${stateRoot}: ${residue.join(', ')}\n`);
|
|
350
350
|
return 1;
|
|
351
351
|
}
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
-
io.stderr.write(`
|
|
354
|
+
io.stderr.write(`pactive: ${action} ${host} ok (${pkgRoot})\n`);
|
|
355
355
|
return 0;
|
|
356
356
|
});
|
|
357
357
|
}
|
|
@@ -361,7 +361,7 @@ function runHost(host, userArgs, env, io) {
|
|
|
361
361
|
const exec = mod.resolveExec({ env });
|
|
362
362
|
const pkgRoot = resolvePkgRoot(env);
|
|
363
363
|
const argv = mod.buildArgv({ pkgRoot, userArgs });
|
|
364
|
-
io.stderr.write(`
|
|
364
|
+
io.stderr.write(`pactive: launching ${host} (${exec})\n`);
|
|
365
365
|
return new Promise((resolve) => {
|
|
366
366
|
const child = spawn(exec, argv, { stdio: 'inherit', env });
|
|
367
367
|
const forward = (sig) => {
|
|
@@ -370,7 +370,7 @@ function runHost(host, userArgs, env, io) {
|
|
|
370
370
|
process.on('SIGINT', forward);
|
|
371
371
|
process.on('SIGTERM', forward);
|
|
372
372
|
child.on('error', (err) => {
|
|
373
|
-
io.stderr.write(`
|
|
373
|
+
io.stderr.write(`pactive: failed to launch ${host}: ${err.message}\n`);
|
|
374
374
|
resolve(err.code === 'ENOENT' ? 127 : 1);
|
|
375
375
|
});
|
|
376
376
|
child.on('exit', (code, signal) => {
|
|
@@ -392,7 +392,7 @@ export async function main(argv, env = process.env, io = { stdout: process.stdou
|
|
|
392
392
|
switch (cmd) {
|
|
393
393
|
case '--version':
|
|
394
394
|
case '-v':
|
|
395
|
-
io.stdout.write(`${
|
|
395
|
+
io.stdout.write(`${pactiveVersion()}\n`);
|
|
396
396
|
return 0;
|
|
397
397
|
case '--help':
|
|
398
398
|
case '-h':
|
|
@@ -432,8 +432,8 @@ if (isMain) {
|
|
|
432
432
|
try {
|
|
433
433
|
process.exitCode = await main(process.argv, process.env, { stdout: process.stdout, stderr: process.stderr });
|
|
434
434
|
} catch (err) {
|
|
435
|
-
const exitCode = err instanceof
|
|
436
|
-
process.stderr.write(`
|
|
435
|
+
const exitCode = err instanceof PactiveError ? err.exitCode : 1;
|
|
436
|
+
process.stderr.write(`pactive: ${err?.message ?? err}\n`);
|
|
437
437
|
process.exitCode = exitCode;
|
|
438
438
|
}
|
|
439
439
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Pactive 主动机制 — status、schedule、tell、history(经 pactive MCP server)
|
|
3
|
+
argument-hint: "[status|schedule|tell|history] [args...]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# pactive:pactive
|
|
7
|
+
|
|
8
|
+
把用户关于「主动机制 / 空闲触发 / 定时任务 / 跨会话消息」的请求路由到 `pactive` MCP server。
|
|
9
|
+
宿主把它暴露为 `/pactive:pactive`;裸 `/pactive` 不由插件契约保证。
|
|
10
|
+
|
|
11
|
+
## 工具名
|
|
12
|
+
|
|
13
|
+
OPCteam 用命名空间暴露插件 MCP 工具:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
mcp__plugin_pactive_pactive__<tool>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
具体名字:
|
|
20
|
+
|
|
21
|
+
- `mcp__plugin_pactive_pactive__pactive_status`
|
|
22
|
+
- `mcp__plugin_pactive_pactive__pactive_schedule`
|
|
23
|
+
- `mcp__plugin_pactive_pactive__pactive_tell`
|
|
24
|
+
- `mcp__plugin_pactive_pactive__pactive_history`
|
|
25
|
+
|
|
26
|
+
裸名(`pactive_status`)不一定能被 ToolSearch 发现;若命名空间工具没加载,用
|
|
27
|
+
`select:mcp__plugin_pactive_pactive__pactive_status` 搜一次。
|
|
28
|
+
|
|
29
|
+
## 路由
|
|
30
|
+
|
|
31
|
+
| 用户说 | 调用 |
|
|
32
|
+
|---|---|
|
|
33
|
+
| 状态 / 为什么没触发 | `pactive_status`(看 `idle.reason`,别猜) |
|
|
34
|
+
| 10 分钟后提醒我 / 定时 | `pactive_schedule` `when:"10m"` `prompt:"…"` |
|
|
35
|
+
| 18:30 提醒我 | `pactive_schedule` `when:"18:30"` |
|
|
36
|
+
| 每小时 | `pactive_schedule` `when:"every 1h"` |
|
|
37
|
+
| 告诉另一个会话 | `pactive_tell` `to` `body`(可选 `kind:"ask"`) |
|
|
38
|
+
| 最近发生过什么 | `pactive_history` `limit` |
|
|
39
|
+
|
|
40
|
+
## 硬规则
|
|
41
|
+
|
|
42
|
+
1. **不要替用户改配置**:pactive 的开关与配置是人工闸门(CLI `--confirm`)。用户要求改配置时,
|
|
43
|
+
告诉他用 `pactive config set … --confirm`,不要自行绕过。
|
|
44
|
+
2. **回执如实转述**:`queued` 就是**还没送到**(对端离线),不要说成已送达。
|
|
45
|
+
3. **默认不打扰**:空闲触发默认关闭;用户没明确要「空闲自动推进」时,不要假设它已开启。
|
|
46
|
+
4. **只报新事实**:状态类输出要给出 `idle.reason` 与护栏结论,不要泛泛说「一切正常」。
|
package/dsh/cordis.patch.yml
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# DSH bundle overlay for the
|
|
1
|
+
# DSH bundle overlay for the pactive package.
|
|
2
2
|
#
|
|
3
3
|
# Format (verified against DSH 0.2.1-rc.5 by running
|
|
4
4
|
# `dsh plugin --profile <p> add <pkg>`): a **top-level YAML array** of loader
|
|
5
5
|
# patch entries; each entry may carry an `insert` list of plugin rows
|
|
6
|
-
# ({ id, name, config }). `name` must be the installed package name (`
|
|
6
|
+
# ({ id, name, config }). `name` must be the installed package name (`pactive`).
|
|
7
7
|
- insert:
|
|
8
|
-
- id:
|
|
9
|
-
name:
|
|
8
|
+
- id: pactive
|
|
9
|
+
name: pactive/dsh
|
|
10
10
|
config: {}
|