niahere 0.4.1 → 0.4.3

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.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "A personal AI assistant daemon — chat, scheduled jobs, persona system, extensible via skills.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -44,9 +44,9 @@
44
44
  "license": "MIT",
45
45
  "private": false,
46
46
  "dependencies": {
47
- "@anthropic-ai/claude-agent-sdk": "^0.2.126",
48
- "@anthropic-ai/sdk": "^0.88.0",
49
- "@modelcontextprotocol/sdk": "^1.27.1",
47
+ "@anthropic-ai/claude-agent-sdk": "^0.3.190",
48
+ "@anthropic-ai/sdk": "^0.105.0",
49
+ "@modelcontextprotocol/sdk": "^1.29.0",
50
50
  "@slack/bolt": "^4.6.0",
51
51
  "cron-parser": "^5.5.0",
52
52
  "grammy": "^1.41.1",
@@ -1,5 +1,5 @@
1
1
  import type { AgentEvent, Normalizer } from "../types";
2
- import { truncate, formatToolUse } from "../../utils/format-activity";
2
+ import { truncate } from "../../utils/format-activity";
3
3
  import { isRetryableApiError, isProviderDownError } from "../../utils/retry";
4
4
 
5
5
  /**
@@ -30,22 +30,14 @@ export class SdkNormalizer implements Normalizer {
30
30
  }
31
31
 
32
32
  if (msg.type === "tool_use_summary") {
33
- return [
34
- {
35
- type: "tool",
36
- name: msg.tool_name || "tool",
37
- summary: formatToolUse(msg.tool_name || "tool", msg.tool_input),
38
- },
39
- ];
33
+ // The SDK provides a ready-made human-readable summary (e.g. "Read foo.ts").
34
+ // (Older code read tool_name/tool_input, which this event does not carry.)
35
+ return msg.summary ? [{ type: "tool", name: "tool", summary: truncate(msg.summary, 70) }] : [];
40
36
  }
41
37
 
42
38
  if (msg.type === "tool_progress") {
43
- if (msg.tool_name === "Bash" && msg.content) {
44
- return [{ type: "tool", name: "Bash", summary: `$ ${truncate(msg.content, 60)}` }];
45
- }
46
- if (msg.content) {
47
- return [{ type: "tool", name: msg.tool_name || "tool", summary: truncate(msg.content, 70) }];
48
- }
39
+ // Carries only tool_use_id/tool_name/elapsed_time no displayable content,
40
+ // and fires repeatedly. tool_use_summary already covers tool activity.
49
41
  return [];
50
42
  }
51
43
 
@@ -151,8 +151,14 @@ class ClaudeSession implements AgentSession {
151
151
  retry = true;
152
152
  break;
153
153
  }
154
- yield ev;
155
- if (ev.type === "result" || ev.type === "error") {
154
+ // A retryable error that survived all our retries means the provider is
155
+ // effectively down for us flag it so the consumer can fail over.
156
+ const out =
157
+ ev.type === "error" && ev.retryable && this.retryCount >= MAX_SEND_RETRIES
158
+ ? { ...ev, providerDown: true }
159
+ : ev;
160
+ yield out;
161
+ if (out.type === "result" || out.type === "error") {
156
162
  this.retryCount = 0;
157
163
  return;
158
164
  }
@@ -4,10 +4,15 @@ import type { SaveMessageParams, RoomStats, RecentMessage, SearchResult, Session
4
4
 
5
5
  export type DeliveryStatus = "pending" | "sent" | "failed";
6
6
 
7
- export async function save(params: SaveMessageParams & { deliveryStatus?: DeliveryStatus; metadata?: Record<string, unknown> }): Promise<number> {
7
+ export async function save(
8
+ params: SaveMessageParams & { deliveryStatus?: DeliveryStatus; metadata?: Record<string, unknown> },
9
+ ): Promise<number> {
8
10
  const sql = getSql();
9
11
  const status = params.deliveryStatus || "sent";
10
- const meta = params.metadata ? JSON.stringify(params.metadata) : null;
12
+ // Pass the object straight through: the postgres driver serializes it to a
13
+ // real jsonb object. Pre-stringifying stores a double-encoded string scalar,
14
+ // which silently breaks every `metadata->>'key'` filter (e.g. notifications).
15
+ const meta = params.metadata ? sql.json(params.metadata as Parameters<typeof sql.json>[0]) : null;
11
16
  const rows = await sql`
12
17
  INSERT INTO messages (session_id, room, sender, content, is_from_agent, delivery_status, metadata)
13
18
  VALUES (${params.sessionId}, ${params.room}, ${params.sender}, ${params.content}, ${params.isFromAgent}, ${status}, ${meta})
@@ -21,7 +26,9 @@ export async function updateDeliveryStatus(id: number, status: DeliveryStatus):
21
26
  await sql`UPDATE messages SET delivery_status = ${status} WHERE id = ${id}`;
22
27
  }
23
28
 
24
- export async function getUndelivered(room?: string): Promise<Array<{ id: number; room: string; content: string; createdAt: string }>> {
29
+ export async function getUndelivered(
30
+ room?: string,
31
+ ): Promise<Array<{ id: number; room: string; content: string; createdAt: string }>> {
25
32
  const sql = getSql();
26
33
  const rows = room
27
34
  ? await sql`
@@ -141,7 +141,10 @@ export async function accumulateMetadata(id: string, resultMeta: Record<string,
141
141
  }
142
142
  }
143
143
 
144
- const delta = JSON.stringify({
144
+ // Bind deltas as jsonb via sql.json — pre-stringifying would store a
145
+ // double-encoded string scalar, making every `->>` extraction return NULL
146
+ // and silently zeroing all accumulated totals.
147
+ const delta = sql.json({
145
148
  total_cost_usd: (resultMeta.cost_usd as number) || 0,
146
149
  total_turns: (resultMeta.turns as number) || 0,
147
150
  total_duration_ms: (resultMeta.duration_ms as number) || 0,
@@ -152,22 +155,23 @@ export async function accumulateMetadata(id: string, resultMeta: Record<string,
152
155
  total_cache_creation_tokens: cacheCreationTokens,
153
156
  message_count: 1,
154
157
  models_used: newModels,
155
- channel: resultMeta.channel || null,
158
+ channel: (resultMeta.channel as string) || null,
156
159
  });
160
+ const modelsDelta = sql.json(newModels);
157
161
 
158
162
  // Atomic accumulate — no read-then-write race
159
163
  await sql`
160
164
  UPDATE sessions SET metadata = jsonb_build_object(
161
- 'total_cost_usd', COALESCE((metadata->>'total_cost_usd')::real, 0) + (${delta}::jsonb->>'total_cost_usd')::real,
162
- 'total_turns', COALESCE((metadata->>'total_turns')::int, 0) + (${delta}::jsonb->>'total_turns')::int,
163
- 'total_duration_ms', COALESCE((metadata->>'total_duration_ms')::real, 0) + (${delta}::jsonb->>'total_duration_ms')::real,
164
- 'total_duration_api_ms', COALESCE((metadata->>'total_duration_api_ms')::real, 0) + (${delta}::jsonb->>'total_duration_api_ms')::real,
165
- 'total_input_tokens', COALESCE((metadata->>'total_input_tokens')::int, 0) + (${delta}::jsonb->>'total_input_tokens')::int,
166
- 'total_output_tokens', COALESCE((metadata->>'total_output_tokens')::int, 0) + (${delta}::jsonb->>'total_output_tokens')::int,
167
- 'total_cache_read_tokens', COALESCE((metadata->>'total_cache_read_tokens')::int, 0) + (${delta}::jsonb->>'total_cache_read_tokens')::int,
168
- 'total_cache_creation_tokens', COALESCE((metadata->>'total_cache_creation_tokens')::int, 0) + (${delta}::jsonb->>'total_cache_creation_tokens')::int,
165
+ 'total_cost_usd', COALESCE((metadata->>'total_cost_usd')::real, 0) + (${delta}->>'total_cost_usd')::real,
166
+ 'total_turns', COALESCE((metadata->>'total_turns')::int, 0) + (${delta}->>'total_turns')::int,
167
+ 'total_duration_ms', COALESCE((metadata->>'total_duration_ms')::real, 0) + (${delta}->>'total_duration_ms')::real,
168
+ 'total_duration_api_ms', COALESCE((metadata->>'total_duration_api_ms')::real, 0) + (${delta}->>'total_duration_api_ms')::real,
169
+ 'total_input_tokens', COALESCE((metadata->>'total_input_tokens')::int, 0) + (${delta}->>'total_input_tokens')::int,
170
+ 'total_output_tokens', COALESCE((metadata->>'total_output_tokens')::int, 0) + (${delta}->>'total_output_tokens')::int,
171
+ 'total_cache_read_tokens', COALESCE((metadata->>'total_cache_read_tokens')::int, 0) + (${delta}->>'total_cache_read_tokens')::int,
172
+ 'total_cache_creation_tokens', COALESCE((metadata->>'total_cache_creation_tokens')::int, 0) + (${delta}->>'total_cache_creation_tokens')::int,
169
173
  'message_count', COALESCE((metadata->>'message_count')::int, 0) + 1,
170
- 'models_used', COALESCE(metadata->'models_used', '[]'::jsonb) || ${JSON.stringify(newModels)}::jsonb,
174
+ 'models_used', COALESCE(metadata->'models_used', '[]'::jsonb) || ${modelsDelta},
171
175
  'channel', COALESCE(metadata->>'channel', ${(resultMeta.channel as string) || null})
172
176
  )
173
177
  WHERE id = ${id}