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.
@@ -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` 与护栏结论,不要泛泛说「一切正常」。
@@ -1,10 +1,10 @@
1
- # DSH bundle overlay for the acti package.
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 (`acti`).
6
+ # ({ id, name, config }). `name` must be the installed package name (`pactive`).
7
7
  - insert:
8
- - id: acti
9
- name: acti/dsh
8
+ - id: pactive
9
+ name: pactive/dsh
10
10
  config: {}
package/dsh/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Acti — DSH (Cordis) plugin.
2
+ * Pactive — DSH (Cordis) plugin.
3
3
  *
4
- * Registers the acti tool surface (`acti_status` / `acti_schedule`) and the
5
- * `/acti` command, and subscribes to session events so the acti activity
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 = "acti";
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", `acti: markActivity(${kind}) failed`, { error: errorText(err) });
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", "acti: markControlReturned failed", { error: errorText(err) });
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: "acti_status",
98
+ name: "pactive_status",
91
99
  description:
92
- "Acti 主动机制状态:空闲判定与 reason、护栏四闸门、定时任务数、最近活动。只读。",
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: "acti_schedule",
124
- description: "登记一个定时任务(真正落盘)。when 支持时长(10m)、时刻(18:30)、ISO 时间、every 1h。",
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: "acti",
165
- description: "Acti:status | tasks | schedule <when> <prompt> | help",
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
- `acti: idle=${idle.idle} reason=${idle.reason} guard=${guard.pass ? "pass" : "blocked"} ` +
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
- ? "acti: 没有定时任务"
195
- : `acti: ${list.length} 个定时任务\n` +
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("用法:/acti schedule <when> <prompt>");
254
+ if (prompt === "") return fail("用法:/pactive schedule <when> <prompt>");
203
255
  const spec = parseWhen(when, now);
204
- if (!spec) return fail(`acti: 无法解析 when(${when})`);
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(`acti: 已登记定时任务 ${created?.id ?? "-"}(${when})`);
267
+ return ok(`pactive: 已登记定时任务 ${created?.id ?? "-"}(${when})`);
216
268
  }
217
- return fail("用法:/acti status | tasks | schedule <when> <prompt> | help");
269
+ return fail("用法:/pactive status | tasks | schedule <when> <prompt> | help");
218
270
  } catch (err) {
219
- return fail(`acti 失败:${errorText(err)}`);
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", `acti: session event ${event} unavailable`, { error: errorText(err) });
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", `acti: session event ${event} unavailable`, { error: errorText(err) });
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", "acti: failed to register tools/commands", { error: errorText(err) });
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", "acti: ctx.effect unavailable; control flag updated on events only", {
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", "acti plugin loaded", { config: pluginConfig });
336
+ safeLog(ctx, "info", "pactive plugin loaded", { config: pluginConfig });
285
337
  }
@@ -13,7 +13,7 @@ await readStdinJson();
13
13
  try {
14
14
  await markActivity("tool");
15
15
  } catch (err) {
16
- process.stderr.write(`[acti] PostToolUse: ${String(err?.message ?? err)}\n`);
16
+ process.stderr.write(`[pactive] PostToolUse: ${String(err?.message ?? err)}\n`);
17
17
  }
18
18
 
19
19
  writeHookOutput({});
@@ -13,7 +13,7 @@ await readStdinJson();
13
13
  try {
14
14
  await markActivity("session-end");
15
15
  } catch (err) {
16
- process.stderr.write(`[acti] SessionEnd: ${String(err?.message ?? err)}\n`);
16
+ process.stderr.write(`[pactive] SessionEnd: ${String(err?.message ?? err)}\n`);
17
17
  }
18
18
 
19
19
  writeHookOutput({});
@@ -14,7 +14,7 @@ await readStdinJson();
14
14
  try {
15
15
  await markActivity("session-start");
16
16
  } catch (err) {
17
- process.stderr.write(`[acti] SessionStart: ${String(err?.message ?? err)}\n`);
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 = `acti: ${tasks.length} 个定时任务到期\n${lines.join("\n")}`;
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(`[acti] Stop: ${String(err?.message ?? err)}\n`);
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(`[acti] UserPromptSubmit: ${String(err?.message ?? err)}\n`);
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(`[acti] UserPromptSubmit (reset): ${String(err?.message ?? err)}\n`);
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
- * `acti` MCP server for OPCteam — stdio JSON-RPC, zero third-party dependencies.
3
+ * `pactive` MCP server for OPCteam — stdio JSON-RPC, zero third-party dependencies.
4
4
  *
5
- * Exposes: acti_status / acti_schedule / acti_tell / acti_history.
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 = "acti";
18
- const SERVER_VERSION = "0.0.1-alpha.1";
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) {
@@ -1,11 +1,11 @@
1
1
  /**
2
- * Shared Acti core access for the OPCteam / DSH host surfaces.
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.ACTI_CORE_MODULE;
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
- `acti: core module not found (tried: ${tried.join(", ")}). ` +
54
- `Build src/core before using this surface; set ACTI_CORE_MODULE to override.`,
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(`acti: state write failed (${errorText(err)})`);
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 `acti` OPCteam plugin (stdio JSON-RPC server).
2
+ * MCP tool surface of the `pactive` OPCteam plugin (stdio JSON-RPC server).
3
3
  *
4
- * Tools: acti_status / acti_schedule / acti_tell / acti_history.
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: "acti_status",
36
+ name: "pactive_status",
37
37
  description:
38
- "Acti 主动机制状态:空闲判定与 reason、护栏四闸门、定时任务数、最近活动。只读。",
38
+ "当用户问「我现在闲不闲」「主动提醒开启了吗」「为什么没触发」「刚才主动做了什么」时用。" +
39
+ "→ 读当前空闲判定、reason、护栏四闸门、任务数与最近活动。只读,不落盘。",
39
40
  inputSchema: obj({}, []),
40
41
  },
41
42
  {
42
- name: "acti_schedule",
43
+ name: "pactive_config",
43
44
  description:
44
- "登记一个定时任务(真正落盘)。when 支持时长(10m)、时刻(18:30)、ISO 时间、every 1h。",
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: "acti_tell",
70
+ name: "pactive_tell",
55
71
  description:
56
- "给同机另一个会话投递一条消息。对端离线时进入 outbox 排队,不伪装成功。",
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: "acti_history",
68
- description: "读取 Acti 事件账本尾部(触发→投递→回应→结账),用于追溯主动行为。",
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 "acti_status": {
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 "acti_schedule": {
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 "acti_tell": {
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 "acti_history": {
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.1-alpha.1",
4
- "description": "Acti:跨 Pi / OPCteam / DSH 的主动能力运行时(idle 判定默认关闭、人工闸门、事件账本)。专有软件:使用条款见包内 LICENSE(发行物含 Pi 扩展源码 pi/extension.ts)。",
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/acti.mjs",
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/acti"
35
+ "./pi/skills/pactive"
37
36
  ]
38
37
  },
39
38
  "dsh": {