pactive 0.0.1-alpha.1 → 0.0.3-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 +57 -49
- package/agents/{acti-observer.md → pactive-observer.md} +4 -4
- package/bin/pactive.mjs +720 -0
- package/commands/pactive.md +49 -0
- package/dsh/cordis.patch.yml +4 -4
- package/dsh/index.js +77 -25
- 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 +65 -14
- package/package.json +4 -5
- package/pi/extension.ts +98 -36
- package/pi/skills/pactive/SKILL.md +70 -0
- package/skills/pactive/SKILL.md +70 -0
- 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 +11 -7
- 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/bin/acti.mjs +0 -439
- package/commands/acti.md +0 -46
- package/pi/skills/acti/SKILL.md +0 -47
- package/skills/acti/SKILL.md +0 -47
|
@@ -0,0 +1,49 @@
|
|
|
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
|
+
- `mcp__plugin_pactive_pactive__pactive_config`(只读;要改配置时只返回可复制命令)
|
|
26
|
+
|
|
27
|
+
裸名(`pactive_status`)不一定能被 ToolSearch 发现;若命名空间工具没加载,用
|
|
28
|
+
`select:mcp__plugin_pactive_pactive__pactive_status` 搜一次。
|
|
29
|
+
|
|
30
|
+
## 路由
|
|
31
|
+
|
|
32
|
+
| 用户说 | 调用 |
|
|
33
|
+
|---|---|
|
|
34
|
+
| 状态 / 为什么没触发 | `pactive_status`(看 `idle.reason`,别猜) |
|
|
35
|
+
| 10 分钟后提醒我 / 定时 | `pactive_schedule` `when:"10m"` `prompt:"…"` |
|
|
36
|
+
| 18:30 提醒我 | `pactive_schedule` `when:"18:30"` |
|
|
37
|
+
| 每小时 | `pactive_schedule` `when:"every 1h"` |
|
|
38
|
+
| 告诉另一个会话 | `pactive_tell` `to` `body`(可选 `kind:"ask"`) |
|
|
39
|
+
| 最近发生过什么 | `pactive_history` `limit` |
|
|
40
|
+
| 开关 / 阈值 / 安静时间是多少 | `pactive_config` `key`(只读,报当前值与默认值) |
|
|
41
|
+
| 帮我开启主动提醒 / 改阈值 | `pactive_config` `key` `value` → 只拿到 `pactive config set … --confirm`,交给用户执行 |
|
|
42
|
+
|
|
43
|
+
## 硬规则
|
|
44
|
+
|
|
45
|
+
1. **不要替用户改配置**:pactive 的开关与配置是人工闸门(CLI `--confirm`)。用户要求改配置时,
|
|
46
|
+
告诉他用 `pactive config set … --confirm`,不要自行绕过;用户执行后下一轮用 `pactive_status` 复核。
|
|
47
|
+
2. **回执如实转述**:`queued` 就是**还没送到**(对端离线),不要说成已送达。
|
|
48
|
+
3. **默认不打扰**:空闲触发默认关闭;用户没明确要「空闲自动推进」时,不要假设它已开启。
|
|
49
|
+
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: {}
|
package/dsh/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Pactive — DSH (Cordis) plugin.
|
|
3
3
|
*
|
|
4
|
-
* Registers the
|
|
5
|
-
* `/
|
|
4
|
+
* Registers the pactive tool surface (`pactive_status` / `pactive_schedule`) and the
|
|
5
|
+
* `/pactive` command, and subscribes to session events so the pactive activity
|
|
6
6
|
* timeline and the control flag stay accurate.
|
|
7
7
|
*
|
|
8
8
|
* The core module (`src/core/index.mjs`) is loaded lazily by relative path —
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import { loadCore, mutateState, parseWhen, pickState, readState } from "../mcp/tools/_core.mjs";
|
|
20
20
|
|
|
21
|
-
export const name = "
|
|
21
|
+
export const name = "pactive";
|
|
22
22
|
|
|
23
23
|
/** Every Cordis service this plugin touches must be declared here. */
|
|
24
24
|
export const inject = ["tools", "commands", "sessions"];
|
|
@@ -47,7 +47,7 @@ async function markActivity(kind, ctx) {
|
|
|
47
47
|
return next && typeof next === "object" ? next : state;
|
|
48
48
|
});
|
|
49
49
|
} catch (err) {
|
|
50
|
-
safeLog(ctx, "warn", `
|
|
50
|
+
safeLog(ctx, "warn", `pactive: markActivity(${kind}) failed`, { error: errorText(err) });
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -61,11 +61,19 @@ async function markControlReturned(ctx) {
|
|
|
61
61
|
return next && typeof next === "object" ? next : state;
|
|
62
62
|
});
|
|
63
63
|
} catch (err) {
|
|
64
|
-
safeLog(ctx, "warn", "
|
|
64
|
+
safeLog(ctx, "warn", "pactive: markControlReturned failed", { error: errorText(err) });
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
const STATUS_PARAMETERS = { type: "object", properties: {}, additionalProperties: false };
|
|
69
|
+
const CONFIG_PARAMETERS = {
|
|
70
|
+
type: "object",
|
|
71
|
+
properties: {
|
|
72
|
+
key: { type: "string", description: "配置键;缺省返回全部(idle.enabled / idle.thresholdMs / policy.*)" },
|
|
73
|
+
value: { type: "string", description: "想改成的新值;给了就只返回 `pactive config set <key> <value> --confirm`,不落盘" },
|
|
74
|
+
},
|
|
75
|
+
additionalProperties: false,
|
|
76
|
+
};
|
|
69
77
|
const SCHEDULE_PARAMETERS = {
|
|
70
78
|
type: "object",
|
|
71
79
|
properties: {
|
|
@@ -87,9 +95,10 @@ export function apply(ctx, config) {
|
|
|
87
95
|
if (!ctx?.tools?.register) return;
|
|
88
96
|
|
|
89
97
|
ctx.tools.register({
|
|
90
|
-
name: "
|
|
98
|
+
name: "pactive_status",
|
|
91
99
|
description:
|
|
92
|
-
"
|
|
100
|
+
"当用户问「我现在闲不闲」「主动提醒开启了吗」「为什么没触发」「刚才主动做了什么」时用。" +
|
|
101
|
+
"→ 读当前空闲判定、reason、护栏四闸门、任务数与最近活动。只读,不落盘。",
|
|
93
102
|
parameters: STATUS_PARAMETERS,
|
|
94
103
|
async execute(args = {}) {
|
|
95
104
|
try {
|
|
@@ -120,8 +129,11 @@ export function apply(ctx, config) {
|
|
|
120
129
|
});
|
|
121
130
|
|
|
122
131
|
ctx.tools.register({
|
|
123
|
-
name: "
|
|
124
|
-
description:
|
|
132
|
+
name: "pactive_schedule",
|
|
133
|
+
description:
|
|
134
|
+
"当用户说「10 分钟后提醒我喝水」「18:30 提醒我开会」「每小时检查一次」时用。" +
|
|
135
|
+
"→ 登记一条定时任务并立即落盘;when 支持 10m / 18:30 / ISO 8601 / every 1h。" +
|
|
136
|
+
"会写盘,需要人工确认(只在用户明确要求时调用)。",
|
|
125
137
|
parameters: SCHEDULE_PARAMETERS,
|
|
126
138
|
async execute(args = {}) {
|
|
127
139
|
try {
|
|
@@ -155,14 +167,54 @@ export function apply(ctx, config) {
|
|
|
155
167
|
}
|
|
156
168
|
},
|
|
157
169
|
});
|
|
170
|
+
|
|
171
|
+
// Read-only by construction: a write request returns the command, never a write (contract §2.3).
|
|
172
|
+
ctx.tools.register({
|
|
173
|
+
name: "pactive_config",
|
|
174
|
+
description:
|
|
175
|
+
"当用户问「主动提醒的开关开着吗」「阈值是多少」「安静时间是几点」「帮我把主动提醒打开」时用。" +
|
|
176
|
+
"→ 读配置值与默认值;要改配置时只返回一条需要人工确认的可复制命令。只读,不落盘。",
|
|
177
|
+
parameters: CONFIG_PARAMETERS,
|
|
178
|
+
async execute(args = {}) {
|
|
179
|
+
try {
|
|
180
|
+
const core = await loadCore();
|
|
181
|
+
const described = core.config?.describe ? core.config.describe() : { schema: {}, defaults: {} };
|
|
182
|
+
const known = Object.keys(described.defaults);
|
|
183
|
+
const key = typeof args.key === "string" ? args.key.trim() : "";
|
|
184
|
+
const rawValue = args.value === undefined || args.value === null ? "" : String(args.value).trim();
|
|
185
|
+
if (key !== "" && !known.includes(key)) {
|
|
186
|
+
return { ok: false, error: `未知配置键(${key});已知:${known.join(", ")}` };
|
|
187
|
+
}
|
|
188
|
+
const paths = core.resolvePaths(HOST_KIND, process.env);
|
|
189
|
+
const state = (await readState(core, paths)) ?? core.defaultState();
|
|
190
|
+
const values = (key === "" ? known : [key]).map((k) => ({
|
|
191
|
+
key: k,
|
|
192
|
+
value: core.config?.get ? core.config.get(state, k) : null,
|
|
193
|
+
default: described.defaults[k] ?? null,
|
|
194
|
+
humanGated: described.schema?.[k]?.humanGated === true,
|
|
195
|
+
}));
|
|
196
|
+
if (rawValue === "") return { ok: true, readOnly: true, wrote: false, values };
|
|
197
|
+
return {
|
|
198
|
+
ok: false,
|
|
199
|
+
wrote: false,
|
|
200
|
+
reason: "need_human_confirmation",
|
|
201
|
+
command: `pactive config set ${key} ${rawValue} --confirm`,
|
|
202
|
+
hint: "不要自己改状态文件;把 command 原样给用户,等他执行后下一轮用 pactive_status 复核。",
|
|
203
|
+
values,
|
|
204
|
+
};
|
|
205
|
+
} catch (err) {
|
|
206
|
+
return { ok: false, error: errorText(err) };
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
});
|
|
158
210
|
};
|
|
159
211
|
|
|
160
212
|
const registerCommands = () => {
|
|
161
213
|
if (!ctx?.commands?.register) return;
|
|
162
214
|
|
|
163
215
|
ctx.commands.register({
|
|
164
|
-
name: "
|
|
165
|
-
description: "
|
|
216
|
+
name: "pactive",
|
|
217
|
+
description: "Pactive:status | tasks | schedule <when> <prompt> | help",
|
|
166
218
|
input: { hint: "status | tasks | schedule <when> <prompt> | help" },
|
|
167
219
|
async handler(invocation) {
|
|
168
220
|
const raw = String(invocation?.rawInput ?? "").trim();
|
|
@@ -182,7 +234,7 @@ export function apply(ctx, config) {
|
|
|
182
234
|
const guard = core.checkGuard ? core.checkGuard(state, now) : { pass: false, reason: "requires-action" };
|
|
183
235
|
const tasks = core.schedule?.list ? core.schedule.list(state) : (state.tasks ?? []);
|
|
184
236
|
return ok(
|
|
185
|
-
`
|
|
237
|
+
`pactive: idle=${idle.idle} reason=${idle.reason} guard=${guard.pass ? "pass" : "blocked"} ` +
|
|
186
238
|
`(${guard.reason ?? "-"}) tasks=${Array.isArray(tasks) ? tasks.length : 0}`,
|
|
187
239
|
);
|
|
188
240
|
}
|
|
@@ -191,17 +243,17 @@ export function apply(ctx, config) {
|
|
|
191
243
|
const list = Array.isArray(tasks) ? tasks : [];
|
|
192
244
|
return ok(
|
|
193
245
|
list.length === 0
|
|
194
|
-
? "
|
|
195
|
-
: `
|
|
246
|
+
? "pactive: 没有定时任务"
|
|
247
|
+
: `pactive: ${list.length} 个定时任务\n` +
|
|
196
248
|
list.map((task) => `- [${task?.id ?? "?"}] ${task?.prompt ?? task?.content ?? ""}`).join("\n"),
|
|
197
249
|
);
|
|
198
250
|
}
|
|
199
251
|
if (sub === "schedule") {
|
|
200
252
|
const when = parts[1] ?? "10m";
|
|
201
253
|
const prompt = parts.slice(2).join(" ");
|
|
202
|
-
if (prompt === "") return fail("用法:/
|
|
254
|
+
if (prompt === "") return fail("用法:/pactive schedule <when> <prompt>");
|
|
203
255
|
const spec = parseWhen(when, now);
|
|
204
|
-
if (!spec) return fail(`
|
|
256
|
+
if (!spec) return fail(`pactive: 无法解析 when(${when})`);
|
|
205
257
|
const next = await mutateState(core, paths, (current) => {
|
|
206
258
|
const out = core.schedule.add(
|
|
207
259
|
current,
|
|
@@ -212,11 +264,11 @@ export function apply(ctx, config) {
|
|
|
212
264
|
});
|
|
213
265
|
const tasks = core.schedule?.list ? core.schedule.list(next) : (next.tasks ?? []);
|
|
214
266
|
const created = Array.isArray(tasks) ? tasks[tasks.length - 1] : null;
|
|
215
|
-
return ok(`
|
|
267
|
+
return ok(`pactive: 已登记定时任务 ${created?.id ?? "-"}(${when})`);
|
|
216
268
|
}
|
|
217
|
-
return fail("用法:/
|
|
269
|
+
return fail("用法:/pactive status | tasks | schedule <when> <prompt> | help");
|
|
218
270
|
} catch (err) {
|
|
219
|
-
return fail(`
|
|
271
|
+
return fail(`pactive 失败:${errorText(err)}`);
|
|
220
272
|
}
|
|
221
273
|
},
|
|
222
274
|
});
|
|
@@ -247,7 +299,7 @@ export function apply(ctx, config) {
|
|
|
247
299
|
void markActivity(kind, ctx);
|
|
248
300
|
});
|
|
249
301
|
} catch (err) {
|
|
250
|
-
safeLog(ctx, "debug", `
|
|
302
|
+
safeLog(ctx, "debug", `pactive: session event ${event} unavailable`, { error: errorText(err) });
|
|
251
303
|
}
|
|
252
304
|
}
|
|
253
305
|
// The agent settled → control returned to the human.
|
|
@@ -257,7 +309,7 @@ export function apply(ctx, config) {
|
|
|
257
309
|
void markControlReturned(ctx);
|
|
258
310
|
});
|
|
259
311
|
} catch (err) {
|
|
260
|
-
safeLog(ctx, "debug", `
|
|
312
|
+
safeLog(ctx, "debug", `pactive: session event ${event} unavailable`, { error: errorText(err) });
|
|
261
313
|
}
|
|
262
314
|
}
|
|
263
315
|
};
|
|
@@ -267,7 +319,7 @@ export function apply(ctx, config) {
|
|
|
267
319
|
registerCommands();
|
|
268
320
|
subscribeSessionEvents();
|
|
269
321
|
} catch (err) {
|
|
270
|
-
safeLog(ctx, "error", "
|
|
322
|
+
safeLog(ctx, "error", "pactive: failed to register tools/commands", { error: errorText(err) });
|
|
271
323
|
}
|
|
272
324
|
|
|
273
325
|
// The agent stops → control returns to the human.
|
|
@@ -276,10 +328,10 @@ export function apply(ctx, config) {
|
|
|
276
328
|
void markControlReturned(ctx);
|
|
277
329
|
});
|
|
278
330
|
} catch (err) {
|
|
279
|
-
safeLog(ctx, "warn", "
|
|
331
|
+
safeLog(ctx, "warn", "pactive: ctx.effect unavailable; control flag updated on events only", {
|
|
280
332
|
error: errorText(err),
|
|
281
333
|
});
|
|
282
334
|
}
|
|
283
335
|
|
|
284
|
-
safeLog(ctx, "info", "
|
|
336
|
+
safeLog(ctx, "info", "pactive plugin loaded", { config: pluginConfig });
|
|
285
337
|
}
|
package/hooks/post-tool-use.mjs
CHANGED
|
@@ -13,7 +13,7 @@ await readStdinJson();
|
|
|
13
13
|
try {
|
|
14
14
|
await markActivity("tool");
|
|
15
15
|
} catch (err) {
|
|
16
|
-
process.stderr.write(`[
|
|
16
|
+
process.stderr.write(`[pactive] PostToolUse: ${String(err?.message ?? err)}\n`);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
writeHookOutput({});
|
package/hooks/session-end.mjs
CHANGED
|
@@ -13,7 +13,7 @@ await readStdinJson();
|
|
|
13
13
|
try {
|
|
14
14
|
await markActivity("session-end");
|
|
15
15
|
} catch (err) {
|
|
16
|
-
process.stderr.write(`[
|
|
16
|
+
process.stderr.write(`[pactive] SessionEnd: ${String(err?.message ?? err)}\n`);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
writeHookOutput({});
|
package/hooks/session-start.mjs
CHANGED
|
@@ -14,7 +14,7 @@ await readStdinJson();
|
|
|
14
14
|
try {
|
|
15
15
|
await markActivity("session-start");
|
|
16
16
|
} catch (err) {
|
|
17
|
-
process.stderr.write(`[
|
|
17
|
+
process.stderr.write(`[pactive] SessionStart: ${String(err?.message ?? err)}\n`);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
writeHookOutput({});
|
package/hooks/stop.mjs
CHANGED
|
@@ -44,12 +44,12 @@ try {
|
|
|
44
44
|
writeHookOutput({});
|
|
45
45
|
} else {
|
|
46
46
|
const lines = tasks.map((task) => `- [${task?.id ?? "?"}] ${task?.prompt ?? task?.content ?? ""}`.trim());
|
|
47
|
-
const reason = `
|
|
47
|
+
const reason = `pactive: ${tasks.length} 个定时任务到期\n${lines.join("\n")}`;
|
|
48
48
|
await mutateState(core, paths, (current) => ({ ...current, stopBlockCount: 1, controlReturned: false }));
|
|
49
49
|
writeHookOutput({ decision: "block", reason });
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
} catch (err) {
|
|
53
|
-
process.stderr.write(`[
|
|
53
|
+
process.stderr.write(`[pactive] Stop: ${String(err?.message ?? err)}\n`);
|
|
54
54
|
writeHookOutput({});
|
|
55
55
|
}
|
|
@@ -20,7 +20,7 @@ await readStdinJson();
|
|
|
20
20
|
try {
|
|
21
21
|
await markActivity("user");
|
|
22
22
|
} catch (err) {
|
|
23
|
-
process.stderr.write(`[
|
|
23
|
+
process.stderr.write(`[pactive] UserPromptSubmit: ${String(err?.message ?? err)}\n`);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
try {
|
|
@@ -28,7 +28,7 @@ try {
|
|
|
28
28
|
const paths = core.resolvePaths("opcteam", process.env);
|
|
29
29
|
await mutateState(core, paths, (state) => ({ ...state, stopBlockCount: 0 }));
|
|
30
30
|
} catch (err) {
|
|
31
|
-
process.stderr.write(`[
|
|
31
|
+
process.stderr.write(`[pactive] UserPromptSubmit (reset): ${String(err?.message ?? err)}\n`);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
writeHookOutput({});
|
package/mcp/server.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* `
|
|
3
|
+
* `pactive` MCP server for OPCteam — stdio JSON-RPC, zero third-party dependencies.
|
|
4
4
|
*
|
|
5
|
-
* Exposes:
|
|
5
|
+
* Exposes: pactive_status / pactive_schedule / pactive_tell / pactive_history.
|
|
6
6
|
* Protocol: newline-delimited JSON-RPC 2.0 (MCP `initialize` / `tools/list` /
|
|
7
7
|
* `tools/call`), the same wire shape the OPCteam plugin loader expects.
|
|
8
8
|
*/
|
|
@@ -14,8 +14,8 @@ import { fileURLToPath } from "node:url";
|
|
|
14
14
|
import { TOOL_DEFINITIONS, findTool, runTool } from "./tools/definitions.mjs";
|
|
15
15
|
|
|
16
16
|
const DEFAULT_PROTOCOL = "2024-11-05";
|
|
17
|
-
const SERVER_NAME = "
|
|
18
|
-
const SERVER_VERSION = "0.0.
|
|
17
|
+
const SERVER_NAME = "pactive";
|
|
18
|
+
const SERVER_VERSION = "0.0.2-alpha.1";
|
|
19
19
|
|
|
20
20
|
/** @param {NodeJS.WritableStream} output */
|
|
21
21
|
function send(output, message) {
|
package/mcp/tools/_core.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared
|
|
2
|
+
* Shared Pactive core access for the OPCteam / DSH host surfaces.
|
|
3
3
|
*
|
|
4
4
|
* `src/core/index.mjs` is produced by the parallel core worker. It is loaded
|
|
5
5
|
* **lazily by relative path** so a missing (not yet built) core degrades into
|
|
6
6
|
* a readable error instead of breaking plugin load.
|
|
7
7
|
*
|
|
8
|
-
* unverified: the frozen host contract (docs/team/task-cards/ACTI-CONTRACT.md)
|
|
8
|
+
* unverified: the frozen host contract (docs/team/task-cards/ACTI-CONTRACT.md, historical file name)
|
|
9
9
|
* freezes the **export names** of core, but not the argument shapes of
|
|
10
10
|
* `loadState` / `saveState` / `mutateState`. Every call below therefore tries
|
|
11
11
|
* the plausible shapes in order and falls back to an explicit read/compute/write
|
|
@@ -31,7 +31,7 @@ let cachedCore = null;
|
|
|
31
31
|
*/
|
|
32
32
|
export async function loadCore(env = process.env) {
|
|
33
33
|
if (cachedCore) return cachedCore;
|
|
34
|
-
const explicit = env.
|
|
34
|
+
const explicit = env.PACTIVE_CORE_MODULE;
|
|
35
35
|
const candidates = explicit ? [explicit, ...CORE_CANDIDATES] : CORE_CANDIDATES;
|
|
36
36
|
const tried = [];
|
|
37
37
|
for (const candidate of candidates) {
|
|
@@ -50,8 +50,8 @@ export async function loadCore(env = process.env) {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
throw new Error(
|
|
53
|
-
`
|
|
54
|
-
`Build src/core before using this surface; set
|
|
53
|
+
`pactive: core module not found (tried: ${tried.join(", ")}). ` +
|
|
54
|
+
`Build src/core before using this surface; set PACTIVE_CORE_MODULE to override.`,
|
|
55
55
|
);
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -137,7 +137,7 @@ export async function mutateState(core, paths, fn) {
|
|
|
137
137
|
try {
|
|
138
138
|
await writeState(core, paths, after);
|
|
139
139
|
} catch (err) {
|
|
140
|
-
throw new Error(`
|
|
140
|
+
throw new Error(`pactive: state write failed (${errorText(err)})`);
|
|
141
141
|
}
|
|
142
142
|
return after;
|
|
143
143
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MCP tool surface of the `
|
|
2
|
+
* MCP tool surface of the `pactive` OPCteam plugin (stdio JSON-RPC server).
|
|
3
3
|
*
|
|
4
|
-
* Tools:
|
|
4
|
+
* Tools: pactive_status / pactive_schedule / pactive_tell / pactive_history.
|
|
5
5
|
* Zero third-party dependencies — the schemas are plain JSON Schema.
|
|
6
6
|
*
|
|
7
7
|
* unverified: mailbox.send / ledger.tail argument shapes come from the frozen
|
|
@@ -33,15 +33,31 @@ const str = (description) => ({ type: "string", description });
|
|
|
33
33
|
/** @type {Array<{name:string, description:string, inputSchema:object}>} */
|
|
34
34
|
export const TOOL_DEFINITIONS = [
|
|
35
35
|
{
|
|
36
|
-
name: "
|
|
36
|
+
name: "pactive_status",
|
|
37
37
|
description:
|
|
38
|
-
"
|
|
38
|
+
"当用户问「我现在闲不闲」「主动提醒开启了吗」「为什么没触发」「刚才主动做了什么」时用。" +
|
|
39
|
+
"→ 读当前空闲判定、reason、护栏四闸门、任务数与最近活动。只读,不落盘。",
|
|
39
40
|
inputSchema: obj({}, []),
|
|
40
41
|
},
|
|
41
42
|
{
|
|
42
|
-
name: "
|
|
43
|
+
name: "pactive_config",
|
|
43
44
|
description:
|
|
44
|
-
"
|
|
45
|
+
"当用户问「主动提醒的开关开着吗」「阈值是多少」「安静时间是几点」「帮我把主动提醒打开」时用。" +
|
|
46
|
+
"→ 读配置值与默认值;要改配置时只返回一条需要人工确认的可复制命令。只读,不落盘。",
|
|
47
|
+
inputSchema: obj(
|
|
48
|
+
{
|
|
49
|
+
key: str("配置键;缺省返回全部(idle.enabled / idle.thresholdMs / policy.*)"),
|
|
50
|
+
value: str("想改成的新值;给了就只返回 `pactive config set <key> <value> --confirm`,不落盘"),
|
|
51
|
+
},
|
|
52
|
+
[],
|
|
53
|
+
),
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: "pactive_schedule",
|
|
57
|
+
description:
|
|
58
|
+
"当用户说「10 分钟后提醒我喝水」「18:30 提醒我开会」「每小时检查一次」时用。" +
|
|
59
|
+
"→ 登记一条定时任务并立即落盘;when 支持 10m / 18:30 / ISO 8601 / every 1h。" +
|
|
60
|
+
"会写盘,需要人工确认(只在用户明确要求时调用)。",
|
|
45
61
|
inputSchema: obj(
|
|
46
62
|
{
|
|
47
63
|
prompt: str("任务正文(到点注入给会话的内容)"),
|
|
@@ -51,9 +67,11 @@ export const TOOL_DEFINITIONS = [
|
|
|
51
67
|
),
|
|
52
68
|
},
|
|
53
69
|
{
|
|
54
|
-
name: "
|
|
70
|
+
name: "pactive_tell",
|
|
55
71
|
description:
|
|
56
|
-
"
|
|
72
|
+
"当用户说「告诉另一个会话我有空」「问一下隔壁 agent 跑完了吗」时用。" +
|
|
73
|
+
"→ 给同机另一个会话投递消息;对端离线时进入 outbox 排队,不伪装成功。" +
|
|
74
|
+
"会写盘,需要人工确认(只在用户明确要求时调用)。",
|
|
57
75
|
inputSchema: obj(
|
|
58
76
|
{
|
|
59
77
|
to: str("目标会话的 agentId 或名字"),
|
|
@@ -64,8 +82,10 @@ export const TOOL_DEFINITIONS = [
|
|
|
64
82
|
),
|
|
65
83
|
},
|
|
66
84
|
{
|
|
67
|
-
name: "
|
|
68
|
-
description:
|
|
85
|
+
name: "pactive_history",
|
|
86
|
+
description:
|
|
87
|
+
"当用户问「刚才那几次主动提醒都发生了什么」「有没有真的触发过」时用。" +
|
|
88
|
+
"→ 读事件账本尾部(触发→投递→回应→结账)。只读。",
|
|
69
89
|
inputSchema: obj({ limit: { type: "integer", description: "返回条数,默认 20" } }, []),
|
|
70
90
|
},
|
|
71
91
|
];
|
|
@@ -98,7 +118,7 @@ export async function runTool(name, args = {}, options = {}) {
|
|
|
98
118
|
|
|
99
119
|
try {
|
|
100
120
|
switch (name) {
|
|
101
|
-
case "
|
|
121
|
+
case "pactive_status": {
|
|
102
122
|
const state = (await readState(core, paths)) ?? core.defaultState();
|
|
103
123
|
const policy = state.policy ?? {};
|
|
104
124
|
const idle = core.evaluateIdle ? core.evaluateIdle(state, now, policy) : { idle: false, reason: "requires-action" };
|
|
@@ -117,7 +137,38 @@ export async function runTool(name, args = {}, options = {}) {
|
|
|
117
137
|
});
|
|
118
138
|
}
|
|
119
139
|
|
|
120
|
-
case "
|
|
140
|
+
case "pactive_config": {
|
|
141
|
+
// Read-only. A write request never lands: the model gets a copy-pasteable command
|
|
142
|
+
// whose --confirm only a human can type (contract §2.3 / I59).
|
|
143
|
+
const described = core.config?.describe ? core.config.describe() : { schema: {}, defaults: {}, humanGatedKeys: [] };
|
|
144
|
+
const known = Object.keys(described.defaults);
|
|
145
|
+
const key = typeof args.key === "string" ? args.key.trim() : "";
|
|
146
|
+
const rawValue = args.value === undefined || args.value === null ? "" : String(args.value).trim();
|
|
147
|
+
if (key !== "" && !known.includes(key)) {
|
|
148
|
+
return json(`ERROR INVALID_ARGUMENT: 未知配置键(${key});已知:${known.join(", ")}`, true);
|
|
149
|
+
}
|
|
150
|
+
const state = (await readState(core, paths)) ?? core.defaultState();
|
|
151
|
+
const values = (key === "" ? known : [key]).map((k) => ({
|
|
152
|
+
key: k,
|
|
153
|
+
value: core.config?.get ? core.config.get(state, k) : null,
|
|
154
|
+
default: described.defaults[k] ?? null,
|
|
155
|
+
humanGated: described.schema?.[k]?.humanGated === true,
|
|
156
|
+
}));
|
|
157
|
+
if (rawValue === "") {
|
|
158
|
+
return json({ ok: true, readOnly: true, wrote: false, values, stateFile: paths.stateFile });
|
|
159
|
+
}
|
|
160
|
+
return json({
|
|
161
|
+
ok: false,
|
|
162
|
+
readOnly: false,
|
|
163
|
+
wrote: false,
|
|
164
|
+
reason: "need_human_confirmation",
|
|
165
|
+
command: `pactive config set ${key} ${rawValue} --confirm`,
|
|
166
|
+
hint: "不要自己改状态文件;把 command 原样给用户,等他执行后下一轮用 pactive_status 复核。",
|
|
167
|
+
values,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
case "pactive_schedule": {
|
|
121
172
|
const prompt = typeof args.prompt === "string" ? args.prompt.trim() : "";
|
|
122
173
|
if (prompt === "") return json("ERROR INVALID_ARGUMENT: prompt 必填", true);
|
|
123
174
|
const spec = parseWhen(args.when, now);
|
|
@@ -138,7 +189,7 @@ const next = await mutateState(core, paths, (state) => {
|
|
|
138
189
|
});
|
|
139
190
|
}
|
|
140
191
|
|
|
141
|
-
case "
|
|
192
|
+
case "pactive_tell": {
|
|
142
193
|
const to = typeof args.to === "string" ? args.to.trim() : "";
|
|
143
194
|
const body = typeof args.body === "string" ? args.body.trim() : "";
|
|
144
195
|
if (to === "" || body === "") return json("ERROR INVALID_ARGUMENT: to 与 body 必填", true);
|
|
@@ -158,7 +209,7 @@ const next = await mutateState(core, paths, (state) => {
|
|
|
158
209
|
});
|
|
159
210
|
}
|
|
160
211
|
|
|
161
|
-
case "
|
|
212
|
+
case "pactive_history": {
|
|
162
213
|
const limit = Number.isInteger(args.limit) ? args.limit : 20;
|
|
163
214
|
const events = core.ledger?.tail ? core.ledger.tail(paths.ledgerFile, limit) : [];
|
|
164
215
|
return json({ count: Array.isArray(events) ? events.length : 0, events });
|
package/package.json
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pactive",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.0.3-alpha.1",
|
|
4
|
+
"description": "Pactive:跨 Pi / OPCteam / DSH 的主动能力运行时(idle 判定默认关闭、人工闸门、事件账本)。专有软件:使用条款见包内 LICENSE(发行物含 Pi 扩展源码 pi/extension.ts)。",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
|
-
"pactive": "bin/
|
|
9
|
-
"acti": "bin/acti.mjs"
|
|
8
|
+
"pactive": "bin/pactive.mjs"
|
|
10
9
|
},
|
|
11
10
|
"exports": {
|
|
12
11
|
".": "./src/core/index.mjs",
|
|
@@ -33,7 +32,7 @@
|
|
|
33
32
|
"./pi/extension.ts"
|
|
34
33
|
],
|
|
35
34
|
"skills": [
|
|
36
|
-
"./pi/skills/
|
|
35
|
+
"./pi/skills/pactive"
|
|
37
36
|
]
|
|
38
37
|
},
|
|
39
38
|
"dsh": {
|