@pi-unipi/utility 0.2.4 → 0.2.6

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/index.ts +57 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/utility",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "Utility commands and tools for Pi coding agent — lifecycle, diagnostics, cache, analytics, display, batch execution",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/index.ts CHANGED
@@ -38,6 +38,12 @@ const VERSION = getPackageVersion(new URL(".", import.meta.url).pathname);
38
38
  /** Whether we've seen the first user message (for auto badge generation) */
39
39
  let firstMessageSeen = false;
40
40
 
41
+ /** Stored user text from first input, used to build conversation summary after agent responds */
42
+ let firstUserText = "";
43
+
44
+ /** Stored UI context from first input, used to show badge overlay after agent responds */
45
+ let firstInputCtx: any = null;
46
+
41
47
  /** All commands registered by this module */
42
48
  const ALL_COMMANDS = [
43
49
  UTILITY_COMMANDS.CONTINUE,
@@ -106,7 +112,7 @@ export default function (pi: ExtensionAPI) {
106
112
  }
107
113
  });
108
114
 
109
- // First-message hook: auto-generate session name on first user message
115
+ // First-message hook: capture user text for deferred badge generation
110
116
  pi.on("input", async (_event: any, ctx: any) => {
111
117
  // Only trigger on first user message
112
118
  if (firstMessageSeen) return;
@@ -120,8 +126,8 @@ export default function (pi: ExtensionAPI) {
120
126
  const sessionName = pi.getSessionName?.();
121
127
  if (sessionName) return;
122
128
 
123
- // Get first message text for context
124
- const messageText = typeof _event?.content === "string"
129
+ // Store first message text for later use in agent_end
130
+ firstUserText = typeof _event?.content === "string"
125
131
  ? _event.content
126
132
  : Array.isArray(_event?.content)
127
133
  ? _event.content
@@ -130,16 +136,57 @@ export default function (pi: ExtensionAPI) {
130
136
  .join(" ")
131
137
  : "";
132
138
 
133
- // Emit event for subagents to spawn background agent
134
- emitEvent(pi, UNIPI_EVENTS.BADGE_GENERATE_REQUEST, {
135
- source: "input-hook",
136
- conversationSummary: messageText.slice(0, 500),
137
- });
139
+ // Store ctx for badge overlay show after agent responds
140
+ firstInputCtx = ctx;
141
+ });
142
+
143
+ // After agent completes first response, generate badge name with full conversation context
144
+ pi.on("agent_end", async (event: any, _ctx: any) => {
145
+ // Only act if we captured a first input and are waiting for badge generation
146
+ if (!firstInputCtx) return;
147
+ const ctx = firstInputCtx;
148
+ firstInputCtx = null; // consume — only trigger once
149
+
150
+ // Check if a name was already set (e.g. manually) in the meantime
151
+ const sessionName = pi.getSessionName?.();
152
+ if (sessionName) return;
138
153
 
139
154
  // Show badge overlay if UI available
140
155
  if (ctx?.hasUI && !nameBadgeState.isVisible()) {
141
156
  await nameBadgeState.show(pi, ctx);
142
157
  }
158
+
159
+ // Build conversation summary from full message history (user + assistant)
160
+ const messages: any[] = event?.messages ?? [];
161
+ const summaryParts: string[] = [];
162
+
163
+ // Include the user's first message
164
+ if (firstUserText) {
165
+ summaryParts.push(`User: ${firstUserText}`);
166
+ }
167
+
168
+ // Include assistant's response text
169
+ const assistantMsgs = messages.filter((m: any) => m.role === "assistant");
170
+ for (const msg of assistantMsgs) {
171
+ if (Array.isArray(msg.content)) {
172
+ const textParts = msg.content
173
+ .filter((c: any) => c.type === "text")
174
+ .map((c: any) => c.text)
175
+ .join(" ");
176
+ if (textParts) summaryParts.push(`Assistant: ${textParts}`);
177
+ } else if (typeof msg.content === "string" && msg.content) {
178
+ summaryParts.push(`Assistant: ${msg.content}`);
179
+ }
180
+ }
181
+
182
+ // Truncate to reasonable size
183
+ const conversationSummary = summaryParts.join("\n").slice(0, 800);
184
+
185
+ // Emit event for subagents to spawn background agent
186
+ emitEvent(pi, UNIPI_EVENTS.BADGE_GENERATE_REQUEST, {
187
+ source: "input-hook",
188
+ conversationSummary,
189
+ });
143
190
  });
144
191
 
145
192
  // Track command usage
@@ -153,6 +200,8 @@ export default function (pi: ExtensionAPI) {
153
200
  pi.on("session_shutdown", async () => {
154
201
  nameBadgeState.hide();
155
202
  firstMessageSeen = false;
203
+ firstUserText = "";
204
+ firstInputCtx = null;
156
205
  await lifecycle.shutdown("session_shutdown");
157
206
  });
158
207
  }