niahere 0.3.11 → 0.3.12

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": "niahere",
3
- "version": "0.3.11",
3
+ "version": "0.3.12",
4
4
  "description": "A personal AI assistant daemon — chat, scheduled jobs, persona system, extensible via skills.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -15,6 +15,21 @@ function cleanSentinel(text: string): string {
15
15
  return text.replace(/`/g, "").trim();
16
16
  }
17
17
 
18
+ interface SlackReactionClient {
19
+ reactions: {
20
+ add(args: { channel: string; timestamp: string; name: string }): Promise<unknown>;
21
+ };
22
+ }
23
+
24
+ export async function reactToSlackMessage(
25
+ client: SlackReactionClient,
26
+ channel: string,
27
+ timestamp: string,
28
+ name: string,
29
+ ): Promise<void> {
30
+ await client.reactions.add({ channel, timestamp, name });
31
+ }
32
+
18
33
  class SlackChannel implements Channel {
19
34
  name = "slack" as const;
20
35
  private app: App | null = null;
@@ -415,12 +430,11 @@ class SlackChannel implements Channel {
415
430
  }
416
431
 
417
432
  // Add thinking reaction inside the lock so cleanup is guaranteed
418
- await client.reactions
419
- .add({ channel: msg.channel, timestamp: msg.ts, name: "thinking_face" })
433
+ await reactToSlackMessage(client, msg.channel, msg.ts, "thinking_face")
420
434
  .catch((err) => log.debug({ err, channel: msg.channel }, "slack: failed to add thinking reaction"));
421
435
 
422
436
  try {
423
- const { result, messageId } = await state.engine.send(
437
+ const { result, messageId, signal } = await state.engine.send(
424
438
  text,
425
439
  {
426
440
  onActivity(status) {
@@ -430,6 +444,15 @@ class SlackChannel implements Channel {
430
444
  attachments,
431
445
  );
432
446
 
447
+ if (signal === "provider_down") {
448
+ await reactToSlackMessage(client, msg.channel, msg.ts, "skull").catch((err) =>
449
+ log.debug({ err, channel: msg.channel }, "slack: failed to add provider-down reaction"),
450
+ );
451
+ if (messageId) await Message.updateDeliveryStatus(messageId, "sent").catch(() => {});
452
+ log.info({ channel: msg.channel, key, reaction: "skull" }, "slack provider failure sent as reaction");
453
+ return;
454
+ }
455
+
433
456
  const reply = result.trim();
434
457
  const cleaned = cleanSentinel(reply);
435
458
 
@@ -101,7 +101,7 @@ export function buildContentBlocks(text: string, attachments?: Attachment[]): Me
101
101
  /** Convert SDK error text into a channel-safe chat response. */
102
102
  export function formatChatError(rawError: string | null | undefined): string {
103
103
  const error = rawError?.trim();
104
- if (!error || error.toLowerCase() === "unknown error") {
104
+ if (getChatErrorSignal(error) === "provider_down") {
105
105
  return GENERIC_CHAT_ERROR;
106
106
  }
107
107
  if (error === "oauth_org_not_allowed") {
@@ -110,6 +110,11 @@ export function formatChatError(rawError: string | null | undefined): string {
110
110
  return `[error] ${error}`;
111
111
  }
112
112
 
113
+ export function getChatErrorSignal(rawError: string | null | undefined): SendResult["signal"] | undefined {
114
+ const error = rawError?.trim();
115
+ return !error || error.toLowerCase() === "unknown error" ? "provider_down" : undefined;
116
+ }
117
+
113
118
  export function resolveSdkModel(contextModel?: string | null): string | undefined {
114
119
  const model = contextModel || getConfig().model;
115
120
  return model && model !== "default" ? model : undefined;
@@ -559,7 +564,7 @@ export async function createChatEngine(opts: EngineOptions): Promise<ChatEngine>
559
564
  );
560
565
  await ActiveEngine.unregister(room);
561
566
  clearLongRunningTimer();
562
- pending.resolve({ result: errorText, costUsd: 0, turns: 0 });
567
+ pending.resolve({ result: errorText, costUsd: 0, turns: 0, signal: getChatErrorSignal(rawError) });
563
568
  pending = null;
564
569
  retryCount = 0;
565
570
  resetIdleTimer();
@@ -2,6 +2,8 @@ export interface SendResult {
2
2
  result: string;
3
3
  costUsd: number;
4
4
  turns: number;
5
+ /** Optional channel-level signal for transports with richer UI than plain text. */
6
+ signal?: "provider_down";
5
7
  /** DB message ID for delivery status tracking (only set for agent replies) */
6
8
  messageId?: number;
7
9
  }