@jwilger/pi-development-system 0.89.0 → 0.90.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jwilger/pi-development-system",
3
- "version": "0.89.0",
3
+ "version": "0.90.0",
4
4
  "description": "A pi extension package representing a seasoned approach to software development using a full AI SDLC.",
5
5
  "keywords": [
6
6
  "pi-package"
@@ -41,4 +41,4 @@ SOFTWARE.
41
41
  6. Empty scope (I5 review): `orch/runtime.ts` `availableScopedModels` treats an empty `/scoped-models` as no restriction (pi's meaning) instead of "matches nothing", because every devsys agent declares `models:` and would otherwise be unspawnable by default. Pinned models are also re-applied on resume (`resolveInitialSettings`), since a restored session may hold only inherited history.
42
42
  7. Bundled agents outside `src/subagents`: `agents/researcher.md` (devsys `models:` family list) and `agents/reviewer.md` (devsys `models:` list and packet format; description rewritten; upstream `icon` and `modelSuggestions` removed) differ from upstream; `agents/{advisor,implementer,lens-*}.md` are new. The agent file format itself is unchanged.
43
43
  8. Thread cap (I7b.1): `orch/manager.ts` `archiveFinished` replaces the hard "Total thread limit reached" check. At `maxThreads` the oldest finished (completed/failed/stopped) thread without a retained descendant is dropped from the registry to make room; the limit is refused only when every kept thread is live, paused or a parent of one. Upstream kept finished threads forever, so long autonomous runs stalled on their own history. A dropped thread cannot be resumed with `agent_steer` (its session file stays on disk).
44
- 9. Registry writes (OOM fix): `index.ts` `persist` signs the registry with `orch/persist-signature.ts` `registrySignature`, which leaves a starting or running thread's status line and partial output out of the change signature. Upstream wrote the whole registry (every thread, about 12 KB each) on every status change; pi never rewrites session entries, so one session grew to 2.3 GB (5,909 snapshots) and pi died loading it. Writes now happen when a thread is added or removed, starts, settles, pauses or is stopped.
44
+ 9. Registry writes (OOM fix): `index.ts` `persist` signs the registry with `orch/persist-signature.ts` `registrySignature`, which leaves a starting or running thread's status line and partial output out of the change signature. Upstream wrote the whole registry (every thread, about 12 KB each) on every status change; pi never rewrites session entries, so one session grew to 2.3 GB (5,909 snapshots) and pi died loading it. Writes now happen when a thread is added or removed, starts, settles, pauses or is stopped. A second session still reached 2.0 GB (5,552 snapshots) because a live thread's `sessionLeafId`, `inputTokens`, `outputTokens` and `elapsedMs` change every turn too, so the signature now keeps only a live thread's identity, task and state; `orch/manager.ts` `saved()` omits `sessionLeafId` for a live thread (it would be stale by the time a reload restored it) so a resumed thread opens its session file at the latest leaf.
@@ -162,7 +162,10 @@ export class ThreadManager {
162
162
  inputTokens: record.view.inputTokens ?? 0,
163
163
  outputTokens: record.view.outputTokens ?? 0,
164
164
  };
165
- const sessionLeafId = this.sessionLeafId(record);
165
+ // A live thread is not written per turn (see orch/persist-signature.ts), so its saved
166
+ // leaf would be stale by the time a reload restores it; without one, resuming opens
167
+ // the session file at its latest leaf, which is the right place.
168
+ const sessionLeafId = active(view) ? undefined : this.sessionLeafId(record);
166
169
  if (sessionLeafId !== undefined) view.sessionLeafId = sessionLeafId;
167
170
  if (active(view)) view.status = view.state === "running" ? "Working" : "Starting";
168
171
  return {
@@ -6,19 +6,31 @@ const LIVE = new Set(["starting", "running"]);
6
6
  * What decides whether the registry needs writing again.
7
7
  *
8
8
  * pi appends every `appendEntry` to the session file and never rewrites it, and each
9
- * registry entry holds every thread (about 12 KB each). A running thread changes its
10
- * status line on every tool call, so signing the full snapshot wrote hundreds of
11
- * kilobytes per tool call: one session reached 2.3 GB and pi ran out of memory loading
12
- * it. A live thread's status and output are not needed to restore it (a reload marks it
13
- * interrupted), so they stay out of the signature. Starting, settling, pausing and
14
- * adding or removing a thread still write.
9
+ * registry entry holds every thread (hundreds of kilobytes once a session has dozens).
10
+ * A running thread changes on every turn: its status line, partial output, session leaf
11
+ * id and token totals. Signing those wrote a snapshot per tool call: one session
12
+ * reached 2.3 GB, a second 2.0 GB after only the status and output were excluded, and
13
+ * pi ran out of memory loading them. None of it is needed to restore a live thread (a
14
+ * reload marks it interrupted and resumes its session file at the latest leaf), so the
15
+ * signature of a live thread is its identity, task and state only. Starting, settling,
16
+ * pausing and adding or removing a thread still write, and a settled thread's leaf,
17
+ * output and totals are signed in full.
15
18
  */
16
19
  export function registrySignature(threads: readonly SavedThread[]): string {
17
20
  return JSON.stringify(
18
- threads.map((thread) =>
19
- LIVE.has(thread.view.state)
20
- ? { ...thread, view: { ...thread.view, status: "", output: undefined } }
21
- : thread,
22
- ),
21
+ threads.map((thread) => {
22
+ if (!LIVE.has(thread.view.state)) return thread;
23
+ const {
24
+ status,
25
+ output,
26
+ error,
27
+ sessionLeafId,
28
+ inputTokens,
29
+ outputTokens,
30
+ elapsedMs,
31
+ ...stable
32
+ } = thread.view;
33
+ return { ...thread, view: stable };
34
+ }),
23
35
  );
24
36
  }