ework-web 0.10.89 → 0.10.91

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/pi-sessions.ts +24 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ework-web",
3
- "version": "0.10.89",
3
+ "version": "0.10.91",
4
4
  "type": "module",
5
5
  "description": "ework-web — standalone multi-project issue tracker. Local SQLite-backed, no external API dependency. Bun + TypeScript + SSR HTML.",
6
6
  "license": "MIT",
@@ -10,7 +10,10 @@ interface PiEvent {
10
10
  timestamp?: string;
11
11
  message?: {
12
12
  role?: string;
13
- content?: Array<{ type?: string; text?: string; thinking?: string; name?: string; arguments?: unknown; toolCallId?: string; output?: unknown }>;
13
+ toolCallId?: string;
14
+ toolName?: string;
15
+ isError?: boolean;
16
+ content?: Array<{ type?: string; text?: string; thinking?: string; name?: string; arguments?: unknown; id?: string; output?: unknown }>;
14
17
  usage?: { input?: number; output?: number; cacheRead?: number; totalTokens?: number };
15
18
  model?: string;
16
19
  };
@@ -118,6 +121,7 @@ export function buildPiSessionPage(sessionId: string, limit: number, asc = false
118
121
 
119
122
  const cards: PiCard[] = [];
120
123
  const openTools = new Map<string, PiToolCall>();
124
+ const seenContent = new Set<string>();
121
125
  let inputTok = 0, outputTok = 0, cacheTok = 0, peakTok = 0;
122
126
  let cwd = "";
123
127
  let first = "", last = "";
@@ -129,6 +133,13 @@ export function buildPiSessionPage(sessionId: string, limit: number, asc = false
129
133
  }
130
134
  if (e.type !== "message" || !e.message) continue;
131
135
  const m = e.message;
136
+ // pi re-emits identical message content as new events (streaming steps,
137
+ // re-persisted context, repeated tool results). Collapse exact repeats —
138
+ // usage excluded from the key so re-emissions still merge, and their
139
+ // (identical) usage is only counted once.
140
+ const contentKey = `${m.role ?? ""}\u0000${JSON.stringify(m.content ?? [])}`;
141
+ if (seenContent.has(contentKey)) continue;
142
+ seenContent.add(contentKey);
132
143
  const u = m.usage;
133
144
  if (u) {
134
145
  inputTok += u.input ?? 0;
@@ -146,7 +157,7 @@ export function buildPiSessionPage(sessionId: string, limit: number, asc = false
146
157
  else if (p.type === "text" && p.text) card.textParts.push(p.text);
147
158
  else if (p.type === "toolCall") {
148
159
  const tc: PiToolCall = {
149
- id: p.toolCallId ?? `t${openTools.size}`,
160
+ id: p.id ?? `t${openTools.size}`,
150
161
  name: p.name ?? "?",
151
162
  args: (() => { try { return typeof p.arguments === "string" ? p.arguments : JSON.stringify(p.arguments ?? {}); } catch { return "{}"; } })(),
152
163
  };
@@ -155,13 +166,17 @@ export function buildPiSessionPage(sessionId: string, limit: number, asc = false
155
166
  }
156
167
  }
157
168
  cards.push(card);
158
- } else {
159
- for (const p of m.content ?? []) {
160
- if (p.type === "toolResult") {
161
- const key = p.toolCallId ?? [...openTools.keys()].pop();
162
- const tc = key ? openTools.get(key) : undefined;
163
- if (tc) tc.result = resultText(p.output);
164
- }
169
+ } else if (m.role === "toolResult") {
170
+ const payload = (m.content ?? []).filter(p => p.type === "text").map(p => p.text ?? "").join("\n");
171
+ const key = m.toolCallId ?? [...openTools.keys()].pop();
172
+ const tc = key ? openTools.get(key) : undefined;
173
+ const text = (payload || resultText(m.content)).slice(0, 4000);
174
+ const stamped = m.isError ? `⚠️ ${text}` : text;
175
+ if (tc) tc.result = stamped;
176
+ else {
177
+ const orphan: PiToolCall = { id: m.toolCallId ?? `t${openTools.size}`, name: m.toolName ?? "tool", args: "", result: stamped };
178
+ openTools.set(orphan.id, orphan);
179
+ cards.push({ ts: e.timestamp ?? "", role: "assistant", textParts: [], reasoning: [], tools: [orphan] });
165
180
  }
166
181
  }
167
182
  }