pi-pier 0.1.5 → 0.2.0

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.
Files changed (55) hide show
  1. package/package.json +1 -1
  2. package/schemas/role-manifest.schema.json +1 -2
  3. package/src/bootstrap.ts +18 -22
  4. package/src/core/grid-shape.ts +13 -14
  5. package/src/core/subagent.ts +325 -857
  6. package/src/core/terminal.ts +25 -28
  7. package/src/core/todo.ts +47 -53
  8. package/src/gc-core.ts +15 -15
  9. package/src/git-adapter.ts +104 -0
  10. package/src/herdr-client.ts +76 -68
  11. package/src/history-store.ts +70 -36
  12. package/src/index-locks.ts +105 -0
  13. package/src/index-master.ts +113 -0
  14. package/src/index-notices.ts +43 -0
  15. package/src/index-pipe.ts +73 -0
  16. package/src/index-runtime.ts +23 -0
  17. package/src/index-worker.ts +39 -0
  18. package/src/index.ts +134 -386
  19. package/src/ledger.ts +15 -17
  20. package/src/lock-core.ts +15 -29
  21. package/src/manifest-compose.ts +9 -23
  22. package/src/notice-buffer.ts +9 -14
  23. package/src/pane-title.ts +24 -24
  24. package/src/pi-surface.ts +22 -29
  25. package/src/pipe-channel.ts +67 -34
  26. package/src/platform-paths.ts +72 -0
  27. package/src/progress-core.ts +26 -24
  28. package/src/reconcile-core.ts +25 -24
  29. package/src/role-loader.ts +32 -35
  30. package/src/role-manifest.ts +24 -28
  31. package/src/roles/master.json +2 -2
  32. package/src/roles/worker-default.json +3 -3
  33. package/src/runtime-policy.ts +66 -0
  34. package/src/session-tail.ts +46 -45
  35. package/src/settle-wake-core.ts +19 -18
  36. package/src/slim-frame.ts +45 -44
  37. package/src/stale-core.ts +23 -21
  38. package/src/storage-layout.ts +80 -0
  39. package/src/subagent-core.ts +151 -94
  40. package/src/subagent-gc.ts +237 -0
  41. package/src/subagent-git-io.ts +80 -0
  42. package/src/subagent-launch.ts +110 -0
  43. package/src/subagent-poll-loop.ts +285 -0
  44. package/src/subagent-poller.ts +90 -0
  45. package/src/subagent-port.ts +27 -0
  46. package/src/subagent-scope.ts +4 -4
  47. package/src/subagent-session-io.ts +143 -0
  48. package/src/subagent-spawn.ts +156 -0
  49. package/src/terminal-core.ts +56 -54
  50. package/src/todo-core.ts +27 -64
  51. package/src/todo-read-hook.ts +28 -24
  52. package/src/todo-reminder-core.ts +89 -0
  53. package/src/todos-service.ts +36 -25
  54. package/src/tool-gate.ts +7 -28
  55. package/src/vocab.ts +18 -19
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-pier",
3
- "version": "0.1.5",
3
+ "version": "0.2.0",
4
4
  "description": "pier: pi extension that fuses pi agent sessions with the herdr pane/tab orchestrator — todo_write loop closure + interactive subagents in isolated panes",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -39,8 +39,7 @@
39
39
  "todos": {
40
40
  "type": "object",
41
41
  "properties": {
42
- "mode": { "enum": ["serial", "parallel"] },
43
- "reminderLimit": { "type": "integer", "exclusiveMinimum": 0 }
42
+ "mode": { "enum": ["serial", "parallel"] }
44
43
  },
45
44
  "additionalProperties": false
46
45
  }
package/src/bootstrap.ts CHANGED
@@ -1,15 +1,13 @@
1
1
  /**
2
- * 档1 bootstrap:master 进程的 cordis 树根(D78 挂载树 / D80 生态采用 / D81 三分法)。
2
+ * Bootstrap the master Cordis tree (D78 mount topology, D80 ecosystem adoption, D81 split).
3
3
  *
4
- * 形态:Loader 作根插件 → builtins.group 注册(D80③ `cordis:group`)→
5
- * timer(hmr 前置依赖,D80②)→ hmr(仅开发姿态:`--expose-internals` 且
6
- * env PI_HERDR_HMR=1,缺一即跳过——生产路径零 watcher)。
4
+ * Load the group, timer, and HMR plugins in order so declarations resolve and HMR stays a
5
+ * development-only path (`--expose-internals` plus PI_HERDR_HMR=1); production has no watcher.
7
6
  *
8
- * worker 进程不走本模块(C3/D81:短命进程手动 mount,无 loader/hmr)——
9
- * 与 subagent-scope.ts 同为 master 分支动态 import。
7
+ * Workers bypass this module (C3/D81) because short-lived processes mount manually without
8
+ * loader or HMR; this module and subagent-scope.ts are master-only dynamic imports.
10
9
  *
11
- * 后续:core 模块(todo/subagent/terminal/approval/reconcile 族)逐个迁为
12
- * loader entry(热换面 = entry,D80③ 手动 mount 不热换)。
10
+ * Core modules will gradually become loader entries; manual mounts remain non-hot-reloaded (D80③).
13
11
  */
14
12
  import { Context } from '@deepseek-ai/cordis';
15
13
  import Loader from '@deepseek-ai/cordis-plugin-loader';
@@ -23,17 +21,17 @@ export interface BootstrapHooks {
23
21
  }
24
22
 
25
23
  export interface CordisApp {
26
- /** 树根(subagent scope 与 lifecycle effect 都挂这里)。 */
24
+ /** Root owns subagent scopes and lifecycle effects. */
27
25
  root: Context;
28
- /** loader service 是否就绪(false = 降级为裸树,行为同档0)。 */
26
+ /** False preserves the bare-tree fallback, matching the baseline behavior. */
29
27
  loaderReady: boolean;
30
- /** hmr 是否激活(开发姿态才有)。 */
28
+ /** HMR is enabled only in the development posture. */
31
29
  hmrActive: boolean;
32
- /** dispose 账本(D80⑤ hmr 补偿 + D79 反注册共用;session_shutdown 走 disposeAll)。 */
30
+ /** Shared ledger compensates HMR disposal and handles session_shutdown cleanup. */
33
31
  ledger: DisposeLedger;
34
32
  }
35
33
 
36
- /** node 是否带 --expose-internals(hmr 的硬前提,D80①)。 */
34
+ /** HMR requires Node's --expose-internals flag (D80①). */
37
35
  export function detectExposeInternals(): boolean {
38
36
  return process.execArgv.some((a) => a === '--expose-internals' || a.startsWith('--expose-internals='));
39
37
  }
@@ -41,13 +39,12 @@ export function detectExposeInternals(): boolean {
41
39
  export async function createCordisApp(hooks: BootstrapHooks = {}): Promise<CordisApp> {
42
40
  const root = new Context();
43
41
  const ledger = new DisposeLedger();
44
- // 服务注入(cordis 原生 DI):core entry 用 ctx.get('pi-herdr.ledger') 取账本——
45
- // (entry config 过不了活实例,loader 会变换 config;provide/get 是 hmr/timer 同款机制)
42
+ // Inject the ledger through Cordis DI because loader transforms entry config and cannot carry live instances.
46
43
  root.provide('pi-herdr.ledger', ledger);
47
44
  if (hooks.onDispose) {
48
45
  root.effect(() => () => { hooks.onDispose?.(); }, 'session-root');
49
46
  }
50
- // 相对名 entry(./core/...)以本文件目录解析
47
+ // Resolve relative entry names (./core/...) from this module's directory.
51
48
  root.baseUrl = pathToFileURL(path.dirname(fileURLToPath(import.meta.url))).href + '/';
52
49
 
53
50
  let loaderReady = false;
@@ -56,7 +53,7 @@ export async function createCordisApp(hooks: BootstrapHooks = {}): Promise<Cordi
56
53
  await root.plugin(Loader);
57
54
  const withLoader = root as Context & { loader?: { builtins: Record<string, unknown> } };
58
55
  if (withLoader.loader) {
59
- withLoader.loader.builtins.group = Group; // D80③:声明里用 cordis:group 引用
56
+ withLoader.loader.builtins.group = Group; // D80③: declarations resolve cordis:group through this builtin.
60
57
  loaderReady = true;
61
58
  }
62
59
  } catch (err) {
@@ -79,9 +76,8 @@ export async function createCordisApp(hooks: BootstrapHooks = {}): Promise<Cordi
79
76
  });
80
77
  hmrActive = true;
81
78
  console.error('[pi-herdr] hmr active (dev posture: --expose-internals + PI_HERDR_HMR=1)');
82
- // D80⑤(修正后):hmr reload **会**拆旧 fiber 的 effect-disposers(registry.delete)——
83
- // ctx.effect 是 core entry 主拆除机制。本 hook 补的是账本侧:pi-surface 旧世代
84
- // 收割(d87 世代化:重挂后的 disposeKey 只杀登记世代及之前,新世代豁免)。
79
+ // D80⑤: HMR already disposes old fiber effects; this hook additionally retires old pi-surface generations.
80
+ // d87 keeps generations registered after the reload boundary alive; disposeKey retires only older entries.
85
81
  root.on('hmr/reload', (reloads: unknown) => {
86
82
  try {
87
83
  const files: string[] = [];
@@ -93,11 +89,11 @@ export async function createCordisApp(hooks: BootstrapHooks = {}): Promise<Cordi
93
89
  if (n > 0) console.error(`[pi-herdr] ledger: compensated ${n} disposal(s) for ${files.join(', ')}`);
94
90
  }
95
91
  } catch {
96
- /* 补偿尽力而为 */
92
+ /* Best-effort compensation; disposal failures are non-fatal. */
97
93
  }
98
94
  });
99
95
  } catch (err) {
100
- // D80②:hmr 挂失败不致命——降级为零 watcher,树其余部分照常
96
+ // D80②: a failed HMR mount degrades to zero watchers while the rest of the tree remains usable.
101
97
  console.error(`[pi-herdr] hmr mount failed (degraded): ${err instanceof Error ? err.message : String(err)}`);
102
98
  }
103
99
  }
@@ -1,21 +1,21 @@
1
1
  /**
2
- * D97 网格形态:spawn 时挑「最大格子」一律 down(上下拆),让 pane 长成全宽横条。
2
+ * D97: Always split the largest cell downward so spawned panes remain full-width strips.
3
3
  *
4
- * 动因(用户实证):热力把非焦点格压窄后,横条仍保全宽——pane title 静帧
5
- * (slim-frame.ts 同源投影)一行横读;旧「方格」拓扑的非焦点格是竖窄条,
6
- * title 折成一竖列不可读。静帧 overlay 的可见性谓词(长宽 < TUI 下限)与
7
- * 本拓扑配套:横条命中行数下限、存量竖条命中列数下限,两代布局都被接住。
4
+ * Heat reflow narrows unfocused cells, but full-width strips keep the slim-frame title
5
+ * readable on one line. The previous grid topology produced narrow vertical cells whose
6
+ * titles wrapped into unreadable columns. The overlay threshold complements both layouts:
7
+ * new strips cross the row limit, while existing columns cross the column limit.
8
8
  *
9
- * 依据 herdr 0.8.2 源码:ratio = split 节点 first child 份额(split_rect:
10
- * first_w = width*ratio);layout.apply 不能挂接既有 pane(只建新 shell),
11
- * 因此网格形态只能在 spawn 增量分裂时养成,形态之外的尺寸归热力 reflow 管。
9
+ * herdr 0.8.2 defines ratio as the first child's share. Because layout.apply creates a
10
+ * new shell rather than attaching an existing pane, spawn-time incremental splits must
11
+ * establish this topology; heat reflow owns only later sizing.
12
12
  */
13
13
 
14
14
  export type ShapeNode =
15
15
  | { type: 'pane'; paneId: string }
16
16
  | { type: 'split'; direction: string; ratio: number; first: ShapeNode; second: ShapeNode };
17
17
 
18
- /** 容错解析 layout.export 的 root(pane 叶:{type:'pane', pane:{pane_id}})。 */
18
+ /** Tolerate layout.export shape variants because pane leaves may nest pane_id. */
19
19
  export function parseShapeTree(raw: unknown): ShapeNode | null {
20
20
  if (!raw || typeof raw !== 'object') return null;
21
21
  const o = raw as Record<string, unknown>;
@@ -49,7 +49,7 @@ export interface PaneCell {
49
49
  h: number;
50
50
  }
51
51
 
52
- /** 先序计算每个 pane 的格子(终端字符坐标;宽高仅用于比较,取典型 TUI 比例 200×50)。 */
52
+ /** Use a representative TUI aspect ratio so preorder cell comparisons reflect screen area. */
53
53
  export function paneCells(root: ShapeNode, width = 200, height = 50): PaneCell[] {
54
54
  const cells: PaneCell[] = [];
55
55
  const walk = (node: ShapeNode, x: number, y: number, w: number, h: number): void => {
@@ -71,10 +71,9 @@ export function paneCells(root: ShapeNode, width = 200, height = 50): PaneCell[]
71
71
  }
72
72
 
73
73
  /**
74
- * 挑分裂目标:面积最大的格子(并列取先序第一,稳定),一律 down(上下拆)。
75
- * 模型 200×50 下 pane 数上限(热力 MAX_AUTO_LAYOUT_PANES=10)内条高最低 5 行
76
- * ——低于 TUI 可用下限即静帧,正是设计语义(条 = title 帧,点击放大看真身)。
77
- * exclude = 不许动的格子(board 等无 agent 常驻 shell)。
74
+ * Split the largest eligible cell downward; preorder tie-breaking keeps the choice stable.
75
+ * At D97's 200x50 model and ten-pane cap, strips eventually become slim title frames by design.
76
+ * Exclusions protect resident non-agent panes such as the board from worker splits.
78
77
  */
79
78
  export function pickGridSplit(
80
79
  root: ShapeNode,