jetbrains-pi-acp 0.1.7 → 0.1.8

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/README.md CHANGED
@@ -11,7 +11,7 @@ Requires **Node.js 22.19.0+** and a JetBrains IDE with ACP support, such as Inte
11
11
  1. Sign in to your model provider from a terminal:
12
12
 
13
13
  ```bash
14
- npx --yes jetbrains-pi-acp@0.1.7 --setup
14
+ npx --yes jetbrains-pi-acp@0.1.8 --setup
15
15
  ```
16
16
 
17
17
  In pi, run `/login`, complete sign-in, then exit pi and return to the IDE. Credentials are managed by pi.
@@ -23,7 +23,7 @@ Requires **Node.js 22.19.0+** and a JetBrains IDE with ACP support, such as Inte
23
23
  "agent_servers": {
24
24
  "pi": {
25
25
  "command": "npx",
26
- "args": ["--yes", "jetbrains-pi-acp@0.1.7"],
26
+ "args": ["--yes", "jetbrains-pi-acp@0.1.8"],
27
27
  "use_idea_mcp": true,
28
28
  "use_custom_mcp": true
29
29
  }
@@ -52,7 +52,7 @@ The IDE must be able to find `npx`; use its absolute path if needed. Menu locati
52
52
  Append pi options after `--` in the agent's `args`, for example:
53
53
 
54
54
  ```json
55
- ["--yes", "jetbrains-pi-acp@0.1.7", "--", "--model", "provider/model-id", "--thinking", "medium"]
55
+ ["--yes", "jetbrains-pi-acp@0.1.8", "--", "--model", "provider/model-id", "--thinking", "medium"]
56
56
  ```
57
57
 
58
58
  Use configuration options here, not initial prompts, interactive mode, or session-selection arguments. The adapter manages RPC mode and sessions.
package/README.zh-CN.md CHANGED
@@ -11,7 +11,7 @@
11
11
  1. 在终端中登录模型提供商:
12
12
 
13
13
  ```bash
14
- npx --yes jetbrains-pi-acp@0.1.7 --setup
14
+ npx --yes jetbrains-pi-acp@0.1.8 --setup
15
15
  ```
16
16
 
17
17
  在 pi 中运行 `/login`,完成登录后退出 pi 并返回 IDE。凭据由 pi 管理。
@@ -23,7 +23,7 @@
23
23
  "agent_servers": {
24
24
  "pi": {
25
25
  "command": "npx",
26
- "args": ["--yes", "jetbrains-pi-acp@0.1.7"],
26
+ "args": ["--yes", "jetbrains-pi-acp@0.1.8"],
27
27
  "use_idea_mcp": true,
28
28
  "use_custom_mcp": true
29
29
  }
@@ -53,7 +53,7 @@ IDE 必须能找到 `npx`,必要时使用其绝对路径。菜单位置随 IDE
53
53
  在 Agent 的 `args` 末尾添加 `--`,随后传递 pi 参数,例如:
54
54
 
55
55
  ```json
56
- ["--yes", "jetbrains-pi-acp@0.1.7", "--", "--model", "provider/model-id", "--thinking", "medium"]
56
+ ["--yes", "jetbrains-pi-acp@0.1.8", "--", "--model", "provider/model-id", "--thinking", "medium"]
57
57
  ```
58
58
 
59
59
  这里只传配置选项,不传初始提示、交互模式或会话选择参数。RPC 模式和会话由适配器管理。
package/extensions/acp.ts CHANGED
@@ -185,7 +185,7 @@ export default function (pi: any) {
185
185
  return { message: { ...event.message, acpMessageId: id } };
186
186
  }
187
187
  });
188
- const archiveCache = new Map<string, { modified: number; archived: boolean }>();
188
+ const archiveCache = new Map<string, { modified: number; archived: boolean; children: string[]; records: string[] }>();
189
189
  for (const [name, server] of Object.entries(config.mcpServers)) {
190
190
  pi.registerMcpServer(name, server);
191
191
  }
@@ -219,19 +219,42 @@ export default function (pi: any) {
219
219
  const currentPaths = new Set(sessions.map(session => session.path));
220
220
  const idsByPath = new Map(sessions.map(session => [session.path, session.id]));
221
221
  for (const path of archiveCache.keys()) if (!currentPaths.has(path)) archiveCache.delete(path);
222
- ctx.ui.notify(`PI_ACP_SESSIONS:${JSON.stringify(sessions.map(session => {
222
+ const infos = sessions.map(session => {
223
223
  const modified = statSync(session.path).mtimeMs;
224
224
  let cached = archiveCache.get(session.path);
225
225
  if (!cached || cached.modified !== modified) {
226
- const archived = SessionManager.open(session.path).getBranch().findLast((entry: any) =>
226
+ const manager = SessionManager.open(session.path);
227
+ const archived = manager.getBranch().findLast((entry: any) =>
227
228
  entry.type === 'custom' && entry.customType === 'acp_archive')?.data === true;
228
- cached = { modified, archived };
229
+ const entries = manager.getEntries() as any[];
230
+ const children = entries.flatMap(entry =>
231
+ entry.type === 'custom' && entry.customType === 'acp_task' && entry.data?.kind === 'agent' &&
232
+ entry.data.owner === session.id && typeof entry.data.sessionId === 'string' ? [entry.data.sessionId] : []);
233
+ const records = entries.flatMap(entry =>
234
+ entry.type === 'custom' && entry.customType === 'subagents:record' && typeof entry.data?.id === 'string' ? [entry.data.id] : []);
235
+ cached = { modified, archived, children, records };
229
236
  archiveCache.set(session.path, cached);
230
237
  }
231
238
  return { sessionId: session.id, cwd: session.cwd, title: session.name || session.firstMessage,
232
239
  updatedAt: session.modified.toISOString(), path: session.path, archived: cached.archived,
233
240
  ...(idsByPath.has(session.parentSessionPath) ? { forkedFrom: idsByPath.get(session.parentSessionPath) } : {}) };
234
- }))}`, 'info');
241
+ });
242
+ // 子 agent 会话标记 subagent,由适配器从列表中过滤:
243
+ // 1) 适配器自己的子 agent:父会话的 acp_task 引用其 ID(自有格式,稳定);
244
+ // 2) 旧版 @tintinweb/pi-subagents 写在顶层目录的历史会话:名称为 `类型#8位hex`,
245
+ // 且父会话有 id 以该 hex 开头的 subagents:record。
246
+ // 当前 pi-subagents 的子会话在嵌套目录或临时目录,SessionManager.list 本就不返回。
247
+ // shortcut: 规则 2 只为旧包的历史数据,不再有此类会话后可整段删除。
248
+ const children = new Set(sessions.flatMap(session => archiveCache.get(session.path)?.children ?? []));
249
+ const recordsByPath = new Map(sessions.map(session => [session.path, archiveCache.get(session.path)?.records ?? []]));
250
+ const isSubagent = (session: any) => {
251
+ const hex = /#([0-9a-f]{8})$/.exec(session.name ?? '')?.[1];
252
+ return children.has(session.id) || (!!hex && !!session.parentSessionPath &&
253
+ !!recordsByPath.get(session.parentSessionPath)?.some(id => id.startsWith(hex)));
254
+ };
255
+ const subagents = new Set(sessions.filter(isSubagent).map(session => session.id));
256
+ ctx.ui.notify(`PI_ACP_SESSIONS:${JSON.stringify(infos.map(info =>
257
+ subagents.has(info.sessionId) ? { ...info, subagent: true } : info))}`, 'info');
235
258
  },
236
259
  });
237
260
  pi.on('tool_call', async (event: any, ctx: any) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jetbrains-pi-acp",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "description": "连接 JetBrains IDE 与 pi coding agent 的 ACP 适配器",
6
6
  "bin": {
@@ -7,7 +7,7 @@ import { t } from './i18n.js';
7
7
 
8
8
  const executeFile = promisify(execFile);
9
9
 
10
- // 历史会话索引:列表、查找、归档及列表变化订阅。
10
+ // 历史会话索引:列表、查找、归档及列表变化订阅;列表默认不含子 agent 会话,仅内部查找时包含。
11
11
  // 历史由临时启动的 discovery pi 进程通过 /__acp_list 读取。
12
12
  export class SessionIndex {
13
13
  constructor(agent) {
@@ -16,7 +16,7 @@ export class SessionIndex {
16
16
  }
17
17
 
18
18
  async find(sessionId, cwd) {
19
- const { sessions } = await this.list(cwd === undefined ? {} : { cwd }, { includeArchived: true });
19
+ const { sessions } = await this.list(cwd === undefined ? {} : { cwd }, { includeArchived: true, includeSubagents: true });
20
20
  return sessions.find(info => info.sessionId === sessionId && (cwd === undefined || resolve(info.cwd) === resolve(cwd)));
21
21
  }
22
22
 
@@ -31,7 +31,7 @@ export class SessionIndex {
31
31
  }) };
32
32
  }
33
33
 
34
- async read(context, params) {
34
+ async read(context, params, { includeSubagents = false } = {}) {
35
35
  let cwds = [context.cwd];
36
36
  if (params._meta?.jetbrains?.air?.list?.includeWorktrees === true) {
37
37
  try {
@@ -44,10 +44,10 @@ export class SessionIndex {
44
44
  context.history = undefined;
45
45
  await context.pi.request('prompt', { message: `/__acp_list ${JSON.stringify({ all: params.cwd === undefined, cwds })}` });
46
46
  if (!context.history) throw new Error(t('piNoHistory'));
47
- return context.history;
47
+ return includeSubagents ? context.history : context.history.filter(info => !info.subagent);
48
48
  }
49
49
 
50
- async list(params, { includeArchived = false } = {}) {
50
+ async list(params, { includeArchived = false, includeSubagents = false } = {}) {
51
51
  if (params.cursor !== undefined && (typeof params.cursor !== 'string' || !/^\d+$/.test(params.cursor))) {
52
52
  throw invalid(t('listCursorInvalid'));
53
53
  }
@@ -65,7 +65,7 @@ export class SessionIndex {
65
65
  }
66
66
  const created = await this.agent.newSession({ cwd: params.cwd ?? process.cwd(), mcpServers: [] }, { discovery: true });
67
67
  try {
68
- const history = (await this.read(created, params)).filter(info =>
68
+ const history = (await this.read(created, params, { includeSubagents })).filter(info =>
69
69
  archived === 'all' || (info.archived === true) === (archived === 'archived'));
70
70
  const sessions = history.slice(offset, offset + limit);
71
71
  return { sessions, ...(offset + limit < history.length ? { nextCursor: String(offset + limit) } : {}) };