@pi-unipi/notify 0.1.7 → 0.1.8

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 (3) hide show
  1. package/events.ts +29 -25
  2. package/package.json +1 -1
  3. package/tools.ts +7 -14
package/events.ts CHANGED
@@ -56,10 +56,13 @@ export function registerEventListeners(
56
56
  const eventConfig = config.events[eventKey];
57
57
  if (!eventConfig?.enabled) continue;
58
58
 
59
- const handler = async (payload: unknown) => {
59
+ const handler = (payload: unknown) => {
60
60
  const title = `Pi — ${def.label}`;
61
61
  const message = buildEventMessage(eventKey, payload);
62
- await dispatchNotification(pi, title, message, eventConfig.platforms, eventKey, config);
62
+ // Fire-and-forget: don't block the event emitter
63
+ dispatchNotification(pi, title, message, eventConfig.platforms, eventKey, config).catch(
64
+ (err) => console.error(`[notify] Background notification failed for ${eventKey}:`, err)
65
+ );
63
66
  };
64
67
 
65
68
  (pi as any).on(def.hook, handler);
@@ -68,43 +71,44 @@ export function registerEventListeners(
68
71
  // agent_end — custom handler with session name and recap support
69
72
  const agentEndConfig = config.events["agent_end"];
70
73
  if (agentEndConfig?.enabled) {
71
- const handler = async (payload: unknown) => {
74
+ const handler = (payload: unknown) => {
75
+ // Fire-and-forget: build message and dispatch in background,
76
+ // don't block agent_end from completing
72
77
  const sessionName = pi.getSessionName?.();
73
78
  const title = `Pi — ${BUILTIN_EVENTS.agent_end.label}`;
74
- let message: string;
75
79
 
76
80
  if (config.recap.enabled) {
77
- // Recap mode: summarize last assistant message
81
+ // Recap mode: summarize asynchronously, then dispatch
78
82
  const lastText = extractLastAssistantText(payload);
79
83
  if (lastText && sessionCtx?.modelRegistry) {
80
84
  const provider = extractProvider(config.recap.model);
81
85
  const modelId = extractModelId(config.recap.model);
82
86
  const model = sessionCtx.modelRegistry.find(provider, modelId);
83
87
  if (model) {
84
- try {
85
- const apiKeyResult = await sessionCtx.modelRegistry.getApiKeyAndHeaders(model);
86
- const apiKey = apiKeyResult.ok ? (apiKeyResult as { apiKey?: string }).apiKey : undefined;
87
- if (apiKey) {
88
- const recap = await summarizeLastMessage(lastText, apiKey, model.baseUrl, model.api, modelId);
89
- message = sessionName ? `${sessionName}: ${recap}` : recap;
90
- } else {
91
- message = buildAgentEndMessage(sessionName);
92
- }
93
- } catch {
94
- message = buildAgentEndMessage(sessionName);
95
- }
96
- } else {
97
- message = buildAgentEndMessage(sessionName);
88
+ sessionCtx.modelRegistry.getApiKeyAndHeaders(model)
89
+ .then((apiKeyResult) => {
90
+ const apiKey = apiKeyResult.ok ? (apiKeyResult as { apiKey?: string }).apiKey : undefined;
91
+ if (apiKey) {
92
+ return summarizeLastMessage(lastText, apiKey, model.baseUrl, model.api, modelId)
93
+ .then((recap) => sessionName ? `${sessionName}: ${recap}` : recap);
94
+ }
95
+ return buildAgentEndMessage(sessionName);
96
+ })
97
+ .catch(() => buildAgentEndMessage(sessionName))
98
+ .then((message) =>
99
+ dispatchNotification(pi, title, message, agentEndConfig.platforms, "agent_end", config)
100
+ )
101
+ .catch((err) => console.error("[notify] Background agent_end notification failed:", err));
102
+ return;
98
103
  }
99
- } else {
100
- message = buildAgentEndMessage(sessionName);
101
104
  }
102
- } else {
103
- // No recap: use session name based message
104
- message = buildAgentEndMessage(sessionName);
105
105
  }
106
106
 
107
- await dispatchNotification(pi, title, message, agentEndConfig.platforms, "agent_end", config);
107
+ // No recap or recap unavailable: dispatch immediately in background
108
+ const message = buildAgentEndMessage(sessionName);
109
+ dispatchNotification(pi, title, message, agentEndConfig.platforms, "agent_end", config).catch(
110
+ (err) => console.error("[notify] Background agent_end notification failed:", err)
111
+ );
108
112
  };
109
113
 
110
114
  (pi as any).on("agent_end", handler);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/notify",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Cross-platform notification extension for Pi — native OS, Gotify, and Telegram notifications for agent lifecycle events",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/tools.ts CHANGED
@@ -9,7 +9,6 @@ import { Type } from "@sinclair/typebox";
9
9
  import { NOTIFY_TOOLS } from "@pi-unipi/core";
10
10
  import { loadConfig } from "./settings.js";
11
11
  import { dispatchNotification } from "./events.js";
12
- import type { NotifyDispatchResult } from "./types.js";
13
12
 
14
13
  /** Schema for notify_user tool parameters */
15
14
  const NotifyUserSchema = Type.Object({
@@ -43,7 +42,7 @@ export function registerNotifyTools(pi: ExtensionAPI): void {
43
42
  "Send a notification to the user's configured platforms (native OS, Gotify, Telegram, ntfy). " +
44
43
  "Use for critical errors, completion of long-running tasks, or when the user explicitly asked to be notified.",
45
44
  parameters: NotifyUserSchema,
46
- async execute(_toolCallId, params, _signal, _onUpdate, ctx: ExtensionContext) {
45
+ async execute(_toolCallId, params, _signal, _onUpdate, _ctx: ExtensionContext) {
47
46
  const {
48
47
  message,
49
48
  title,
@@ -64,32 +63,26 @@ export function registerNotifyTools(pi: ExtensionAPI): void {
64
63
  // Resolve platforms — use params.platforms or global defaults
65
64
  const notifPlatforms = platforms || config.defaultPlatforms;
66
65
 
67
- // Dispatch notification
68
- const result: NotifyDispatchResult = await dispatchNotification(
66
+ // Fire-and-forget: dispatch in background so the tool doesn't block the agent
67
+ dispatchNotification(
69
68
  pi,
70
69
  notifTitle,
71
70
  message,
72
71
  notifPlatforms,
73
72
  "agent_tool",
74
73
  config
75
- );
76
-
77
- // Format result
78
- const platformResults = result.results.map(
79
- (r) => `${r.platform}: ${r.success ? "✓ sent" : `✗ ${r.error || "failed"}`}`
74
+ ).catch((err) =>
75
+ console.error("[notify] Background notify_user dispatch failed:", err)
80
76
  );
81
77
 
82
78
  return {
83
79
  content: [
84
80
  {
85
81
  type: "text" as const,
86
- text: `Notification sent to ${result.results.length} platform(s):\n${platformResults.join("\n")}`,
82
+ text: `Notification sending to ${notifPlatforms.length} platform(s): ${notifPlatforms.join(", ")}`,
87
83
  },
88
84
  ],
89
- details: {
90
- platforms: result.results.map((r) => r.platform),
91
- allSuccess: result.allSuccess,
92
- },
85
+ details: { platforms: notifPlatforms },
93
86
  };
94
87
  },
95
88
  });