pi-crew 0.9.20 → 0.9.22

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/CHANGELOG.md CHANGED
@@ -1,5 +1,69 @@
1
1
  # Changelog
2
2
 
3
+ ## [v0.9.22] — fix: batch buffered event-log flush (25-85x faster) (2026-07-07)
4
+
5
+ P0 follow-up to v0.9.21 event batching. The buffered flush path was calling `appendEventInsideLock` per-event, which triggered an `fsyncSync` per queued event. This defeated the entire point of batching — for 100 buffered events the flush took ~3s on tmpfs instead of the expected ~20ms.
6
+
7
+ ### Highlights
8
+
9
+ - **Buffered flush now coalesces fsyncs.** New `appendEventBatchInsideLock()` computes metadata for the whole queue, then performs 1 `appendFileSync` + 1 `fsyncSync` + 1 `persistSequence` + 1 cache update at the end. Resolves all queued promises with their finalized events.
10
+ - **Side fixes for batch correctness.** `nextSequence()` cannot be called per-event in Phase 1 (no writes between calls → all events get the same seq). Now computes `startingSeq` once and increments locally. `setTimeout` callback no longer returns a floating Promise from `flushOneEventLogBuffer`. `process.on('exit')` guards `flushEventLogBuffer()` with a size > 0 check to avoid creating a new floating Promise on every exit.
11
+
12
+ ### Performance
13
+
14
+ | Workload | Before | After | Speedup |
15
+ |---|---|---|---|
16
+ | 100 buffered events | 3,314 ms | 132 ms | 25x |
17
+ | 1,000 buffered events | 29,205 ms | 343 ms | 85x |
18
+
19
+ ### Verification
20
+
21
+ | Gate | Result |
22
+ |---|---|
23
+ | `event-log-async.test.ts` | ✅ 16/16 pass |
24
+ | `event-log-batch.test.ts` | ✅ all pass |
25
+ | `event-log-buffered.test.ts` | ✅ seq uniqueness restored |
26
+ | `event-log-race.test.ts` | ✅ all pass |
27
+ | `event-log-leak.test.ts` H2 | ✅ pass |
28
+ | `event-log-leak.test.ts` H1/H3 | ⚠️ cancelled under `--test-force-exit` (node:test framework detects pending work; tracked separately) |
29
+ | `per-task-budget.test.ts`, `token-counter.test.ts` | ✅ all pass |
30
+
31
+ ### Files
32
+
33
+ - MOD: `src/state/event-log.ts` — added `appendEventBatchInsideLock()`, fixed `nextSequence()` batching, fixed floating Promise in `setTimeout` callback, guarded `process.on('exit')` flush
34
+
35
+ ---
36
+
37
+ ## [v0.9.21] — P0 performance fixes: event batching, async worktree, per-task budget, token counter (2026-07-07)
38
+
39
+ Four P0 performance fixes identified by a parallel performance review (4 subagents: runtime, token efficiency, architecture, security overhead). All fixes are backward-compatible; no breaking changes.
40
+
41
+ ### Highlights
42
+
43
+ - **Event log batching (P0).** The event log now uses a 50ms flush interval instead of immediate per-event writes. This reduces fsync calls from 1 per event to 1 per batch. New helpers: `appendEventBuffered()`, `flushEventBuffer()`. Force flush on run completion and shutdown. `src/state/event-log.ts`; 12 tests in `test/unit/event-log-batch.test.ts`.
44
+
45
+ - **Async worktree operations (P0).** `execFileSync` → async `execFile` for non-blocking I/O. `findGitRoot()` cached via Map to avoid repeated disk reads. `assertCleanLeader()` uses async execFile. Dynamic workflow context updated to use async worktree. `src/worktree/worktree-manager.ts`, `src/runtime/task-runner.ts`, `src/runtime/dynamic-workflow-context.ts`; 7 tests in `test/unit/worktree-async.test.ts`.
46
+
47
+ - **Per-task token budget enforcement (P0).** `executeTeamRun()` now enforces `budgetTotal`/`budgetAbort` thresholds. Budget warning and abort events emitted when limits exceeded. `TeamRunManifest` includes `budgetTotal`, `budgetWarning`, `budgetAbort`, `budgetUnlimited` fields. Budget propagation chain: MCP tool → handleRun → executeTeamRun → manifest persistence. `src/runtime/team-runner.ts`, `src/extension/team-tool/run.ts`, `src/state/types.ts`; 14 tests in `test/unit/per-task-budget.test.ts`.
48
+
49
+ - **Accurate token counting (P0).** New `token-counter.ts` with `ceil(alpha/4) + punctuation` formula. Replaces `char/4` heuristic for better accuracy on code. Single-pass O(n) algorithm, no dependencies. Tool output pruner uses `countTokens()` for accurate pruning. `src/utils/token-counter.ts`, `src/runtime/tool-output-pruner.ts`; 9 tests in `test/unit/token-counter.test.ts`.
50
+
51
+ ### Verification
52
+
53
+ | Gate | Result |
54
+ |---|---|
55
+ | All unit tests (21 files) | ✅ 187/187 pass |
56
+ | New P0 tests (4 files) | ✅ 67/67 pass |
57
+ | Live E2E (fast-fix workflow) | ✅ 3/3 tasks, budget propagation verified |
58
+ | Extension load | ✅ |
59
+
60
+ ### Files
61
+
62
+ - NEW: `src/utils/token-counter.ts`, `test/unit/event-log-batch.test.ts`, `test/unit/per-task-budget.test.ts`, `test/unit/token-counter.test.ts`, `test/unit/worktree-async.test.ts`
63
+ - MOD: `src/state/event-log.ts`, `src/worktree/worktree-manager.ts`, `src/runtime/team-runner.ts`, `src/runtime/task-runner.ts`, `src/runtime/dynamic-workflow-context.ts`, `src/runtime/tool-output-pruner.ts`, `src/extension/team-tool/run.ts`, `src/extension/cross-extension-rpc.ts`, `src/runtime/background-runner.ts`, `src/state/types.ts`
64
+
65
+ ---
66
+
3
67
  ## [v0.9.20] — security hardening: RPC HMAC auth + per-task API key scoping + safe-bash whitelist (2026-07-06)
4
68
 
5
69
  Three defense-in-depth security upgrades distilled from cross-source research (52+ repos) and the H-1/H-2/H-6 audit findings. All three are additive (no breaking changes); two are opt-in via env vars, one is default-on.
@@ -6351,7 +6351,7 @@
6351
6351
  "format": "esm"
6352
6352
  },
6353
6353
  "src/state/event-log.ts": {
6354
- "bytes": 37533,
6354
+ "bytes": 44845,
6355
6355
  "imports": [
6356
6356
  {
6357
6357
  "path": "node:crypto",
@@ -7233,7 +7233,7 @@
7233
7233
  "format": "esm"
7234
7234
  },
7235
7235
  "src/utils/env-filter.ts": {
7236
- "bytes": 4485,
7236
+ "bytes": 6523,
7237
7237
  "imports": [
7238
7238
  {
7239
7239
  "path": "src/utils/redaction.ts",
@@ -7341,7 +7341,7 @@
7341
7341
  "format": "esm"
7342
7342
  },
7343
7343
  "src/runtime/child-pi.ts": {
7344
- "bytes": 61943,
7344
+ "bytes": 60312,
7345
7345
  "imports": [
7346
7346
  {
7347
7347
  "path": "node:child_process",
@@ -14125,11 +14125,22 @@
14125
14125
  ],
14126
14126
  "format": "esm"
14127
14127
  },
14128
- "src/runtime/tool-output-pruner.ts": {
14129
- "bytes": 12289,
14128
+ "src/utils/token-counter.ts": {
14129
+ "bytes": 2162,
14130
14130
  "imports": [],
14131
14131
  "format": "esm"
14132
14132
  },
14133
+ "src/runtime/tool-output-pruner.ts": {
14134
+ "bytes": 12320,
14135
+ "imports": [
14136
+ {
14137
+ "path": "src/utils/token-counter.ts",
14138
+ "kind": "import-statement",
14139
+ "original": "../utils/token-counter.ts"
14140
+ }
14141
+ ],
14142
+ "format": "esm"
14143
+ },
14133
14144
  "src/runtime/task-output-context.ts": {
14134
14145
  "bytes": 26832,
14135
14146
  "imports": [
@@ -14358,7 +14369,7 @@
14358
14369
  "format": "esm"
14359
14370
  },
14360
14371
  "src/worktree/worktree-manager.ts": {
14361
- "bytes": 27468,
14372
+ "bytes": 40499,
14362
14373
  "imports": [
14363
14374
  {
14364
14375
  "path": "node:child_process",
@@ -14380,6 +14391,11 @@
14380
14391
  "kind": "import-statement",
14381
14392
  "external": true
14382
14393
  },
14394
+ {
14395
+ "path": "node:util",
14396
+ "kind": "import-statement",
14397
+ "external": true
14398
+ },
14383
14399
  {
14384
14400
  "path": "src/config/config.ts",
14385
14401
  "kind": "import-statement",
@@ -14794,7 +14810,7 @@
14794
14810
  "format": "esm"
14795
14811
  },
14796
14812
  "src/runtime/task-runner.ts": {
14797
- "bytes": 54685,
14813
+ "bytes": 54735,
14798
14814
  "imports": [
14799
14815
  {
14800
14816
  "path": "node:fs",
@@ -15103,7 +15119,7 @@
15103
15119
  "format": "esm"
15104
15120
  },
15105
15121
  "src/runtime/team-runner.ts": {
15106
- "bytes": 70267,
15122
+ "bytes": 75896,
15107
15123
  "imports": [
15108
15124
  {
15109
15125
  "path": "node:fs",
@@ -15631,7 +15647,7 @@
15631
15647
  "format": "esm"
15632
15648
  },
15633
15649
  "src/runtime/dynamic-workflow-context.ts": {
15634
- "bytes": 40912,
15650
+ "bytes": 40944,
15635
15651
  "imports": [
15636
15652
  {
15637
15653
  "path": "node:crypto",
@@ -15768,7 +15784,7 @@
15768
15784
  "format": "esm"
15769
15785
  },
15770
15786
  "src/extension/team-tool/run.ts": {
15771
- "bytes": 42914,
15787
+ "bytes": 44253,
15772
15788
  "imports": [
15773
15789
  {
15774
15790
  "path": "src/agents/discover-agents.ts",
@@ -17096,14 +17112,30 @@
17096
17112
  ],
17097
17113
  "format": "esm"
17098
17114
  },
17115
+ "src/extension/rpc-hmac.ts": {
17116
+ "bytes": 7500,
17117
+ "imports": [
17118
+ {
17119
+ "path": "node:crypto",
17120
+ "kind": "import-statement",
17121
+ "external": true
17122
+ }
17123
+ ],
17124
+ "format": "esm"
17125
+ },
17099
17126
  "src/extension/cross-extension-rpc.ts": {
17100
- "bytes": 10489,
17127
+ "bytes": 10981,
17101
17128
  "imports": [
17102
17129
  {
17103
17130
  "path": "src/utils/safe-paths.ts",
17104
17131
  "kind": "import-statement",
17105
17132
  "original": "../utils/safe-paths.ts"
17106
17133
  },
17134
+ {
17135
+ "path": "src/extension/rpc-hmac.ts",
17136
+ "kind": "import-statement",
17137
+ "original": "./rpc-hmac.ts"
17138
+ },
17107
17139
  {
17108
17140
  "path": "src/runtime/live-control-realtime.ts",
17109
17141
  "kind": "import-statement",
@@ -18166,7 +18198,7 @@
18166
18198
  "imports": [],
18167
18199
  "exports": [],
18168
18200
  "inputs": {},
18169
- "bytes": 6427881
18201
+ "bytes": 6479322
18170
18202
  },
18171
18203
  "dist/index.mjs": {
18172
18204
  "imports": [
@@ -19205,6 +19237,11 @@
19205
19237
  "kind": "import-statement",
19206
19238
  "external": true
19207
19239
  },
19240
+ {
19241
+ "path": "node:util",
19242
+ "kind": "import-statement",
19243
+ "external": true
19244
+ },
19208
19245
  {
19209
19246
  "path": "node:fs",
19210
19247
  "kind": "import-statement",
@@ -19545,6 +19582,11 @@
19545
19582
  "kind": "import-statement",
19546
19583
  "external": true
19547
19584
  },
19585
+ {
19586
+ "path": "node:crypto",
19587
+ "kind": "import-statement",
19588
+ "external": true
19589
+ },
19548
19590
  {
19549
19591
  "path": "@earendil-works/pi-tui",
19550
19592
  "kind": "import-statement",
@@ -20349,7 +20391,7 @@
20349
20391
  "bytesInOutput": 4478
20350
20392
  },
20351
20393
  "src/state/event-log.ts": {
20352
- "bytesInOutput": 22276
20394
+ "bytesInOutput": 26234
20353
20395
  },
20354
20396
  "src/hooks/registry.ts": {
20355
20397
  "bytesInOutput": 4422
@@ -20448,7 +20490,7 @@
20448
20490
  "bytesInOutput": 291
20449
20491
  },
20450
20492
  "src/utils/env-filter.ts": {
20451
- "bytesInOutput": 2272
20493
+ "bytesInOutput": 3511
20452
20494
  },
20453
20495
  "src/runtime/compact-pipeline.ts": {
20454
20496
  "bytesInOutput": 517
@@ -20487,7 +20529,7 @@
20487
20529
  "bytesInOutput": 1642
20488
20530
  },
20489
20531
  "src/runtime/child-pi.ts": {
20490
- "bytesInOutput": 43955
20532
+ "bytesInOutput": 41851
20491
20533
  },
20492
20534
  "src/runtime/heartbeat-gradient.ts": {
20493
20535
  "bytesInOutput": 953
@@ -21377,8 +21419,11 @@
21377
21419
  "src/runtime/goal-achievement.ts": {
21378
21420
  "bytesInOutput": 2922
21379
21421
  },
21422
+ "src/utils/token-counter.ts": {
21423
+ "bytesInOutput": 685
21424
+ },
21380
21425
  "src/runtime/tool-output-pruner.ts": {
21381
- "bytesInOutput": 5528
21426
+ "bytesInOutput": 5527
21382
21427
  },
21383
21428
  "src/runtime/task-output-context.ts": {
21384
21429
  "bytesInOutput": 12672
@@ -21423,7 +21468,7 @@
21423
21468
  "bytesInOutput": 2538
21424
21469
  },
21425
21470
  "src/worktree/worktree-manager.ts": {
21426
- "bytesInOutput": 20059
21471
+ "bytesInOutput": 21100
21427
21472
  },
21428
21473
  "src/runtime/event-stream-bridge.ts": {
21429
21474
  "bytesInOutput": 2321
@@ -21489,7 +21534,7 @@
21489
21534
  "bytesInOutput": 6123
21490
21535
  },
21491
21536
  "src/runtime/task-runner.ts": {
21492
- "bytesInOutput": 42442
21537
+ "bytesInOutput": 42475
21493
21538
  },
21494
21539
  "src/runtime/workflow-state.ts": {
21495
21540
  "bytesInOutput": 2471
@@ -21507,7 +21552,7 @@
21507
21552
  "bytesInOutput": 3398
21508
21553
  },
21509
21554
  "src/runtime/team-runner.ts": {
21510
- "bytesInOutput": 53388
21555
+ "bytesInOutput": 57404
21511
21556
  },
21512
21557
  "src/runtime/pipeline-runner.ts": {
21513
21558
  "bytesInOutput": 11755
@@ -21558,13 +21603,13 @@
21558
21603
  "bytesInOutput": 1460
21559
21604
  },
21560
21605
  "src/runtime/dynamic-workflow-context.ts": {
21561
- "bytesInOutput": 20185
21606
+ "bytesInOutput": 20207
21562
21607
  },
21563
21608
  "src/runtime/dynamic-workflow-runner.ts": {
21564
21609
  "bytesInOutput": 6259
21565
21610
  },
21566
21611
  "src/extension/team-tool/run.ts": {
21567
- "bytesInOutput": 33569
21612
+ "bytesInOutput": 34875
21568
21613
  },
21569
21614
  "src/extension/team-tool.ts": {
21570
21615
  "bytesInOutput": 35293
@@ -21672,7 +21717,7 @@
21672
21717
  "bytesInOutput": 6698
21673
21718
  },
21674
21719
  "src/observability/exporters/otlp-exporter.ts": {
21675
- "bytesInOutput": 7427
21720
+ "bytesInOutput": 7442
21676
21721
  },
21677
21722
  "src/observability/metrics-primitives.ts": {
21678
21723
  "bytesInOutput": 5869
@@ -21753,7 +21798,10 @@
21753
21798
  "bytesInOutput": 1204
21754
21799
  },
21755
21800
  "src/extension/cross-extension-rpc.ts": {
21756
- "bytesInOutput": 7789
21801
+ "bytesInOutput": 8532
21802
+ },
21803
+ "src/extension/rpc-hmac.ts": {
21804
+ "bytesInOutput": 3514
21757
21805
  },
21758
21806
  "src/extension/message-renderers.ts": {
21759
21807
  "bytesInOutput": 2457
@@ -21783,7 +21831,7 @@
21783
21831
  "bytesInOutput": 81
21784
21832
  }
21785
21833
  },
21786
- "bytes": 3083229
21834
+ "bytes": 3097798
21787
21835
  }
21788
21836
  }
21789
21837
  }