kanna-code 0.26.5 → 0.26.7

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.
@@ -15,9 +15,32 @@ import { deriveChatSnapshot, deriveLocalProjectsSnapshot, deriveSidebarData } fr
15
15
 
16
16
  const DEFAULT_CHAT_RECENT_LIMIT = 200
17
17
 
18
+ function isSendToStartingProfilingEnabled() {
19
+ return process.env.KANNA_PROFILE_SEND_TO_STARTING === "1"
20
+ }
21
+
22
+ function logSendToStartingProfile(
23
+ traceId: string | null | undefined,
24
+ startedAt: number | null | undefined,
25
+ stage: string,
26
+ details?: Record<string, unknown>
27
+ ) {
28
+ if (!traceId || startedAt === undefined || startedAt === null || !isSendToStartingProfilingEnabled()) {
29
+ return
30
+ }
31
+
32
+ console.log("[kanna/send->starting][server]", JSON.stringify({
33
+ traceId,
34
+ stage,
35
+ elapsedMs: Number((performance.now() - startedAt).toFixed(1)),
36
+ ...details,
37
+ }))
38
+ }
39
+
18
40
  export interface ClientState {
19
41
  subscriptions: Map<string, SubscriptionTopic>
20
42
  snapshotSignatures: Map<string, string>
43
+ protectedDraftChatIds?: Set<string>
21
44
  }
22
45
 
23
46
  interface CreateWsRouterArgs {
@@ -83,9 +106,28 @@ export function createWsRouter({
83
106
  ])
84
107
  }
85
108
 
86
- async function maybePruneStaleEmptyChats() {
109
+ function getProtectedDraftChatIds(extraSockets?: Iterable<ServerWebSocket<ClientState>>) {
110
+ const protectedChatIds = new Set<string>()
111
+
112
+ for (const socket of sockets) {
113
+ for (const chatId of socket.data.protectedDraftChatIds ?? []) {
114
+ protectedChatIds.add(chatId)
115
+ }
116
+ }
117
+
118
+ for (const socket of extraSockets ?? []) {
119
+ for (const chatId of socket.data.protectedDraftChatIds ?? []) {
120
+ protectedChatIds.add(chatId)
121
+ }
122
+ }
123
+
124
+ return protectedChatIds
125
+ }
126
+
127
+ async function maybePruneStaleEmptyChats(extraSockets?: Iterable<ServerWebSocket<ClientState>>) {
87
128
  await store.pruneStaleEmptyChats?.({
88
129
  activeChatIds: getProtectedChatIds(),
130
+ protectedChatIds: getProtectedDraftChatIds(extraSockets),
89
131
  })
90
132
  }
91
133
 
@@ -194,7 +236,7 @@ export function createWsRouter({
194
236
 
195
237
  async function pushSnapshots(ws: ServerWebSocket<ClientState>, options?: { skipPrune?: boolean }) {
196
238
  if (!options?.skipPrune) {
197
- await maybePruneStaleEmptyChats()
239
+ await maybePruneStaleEmptyChats([ws])
198
240
  }
199
241
  const snapshotSignatures = ensureSnapshotSignatures(ws)
200
242
  for (const [id, topic] of ws.data.subscriptions.entries()) {
@@ -205,6 +247,14 @@ export function createWsRouter({
205
247
  continue
206
248
  }
207
249
  snapshotSignatures.set(id, signature)
250
+ if (topic.type === "chat" && envelope.snapshot.type === "chat" && envelope.snapshot.data?.runtime.status === "starting") {
251
+ const profile = agent.getActiveTurnProfile(topic.chatId)
252
+ logSendToStartingProfile(profile?.traceId, profile?.startedAt, "ws.snapshot_sent", {
253
+ chatId: topic.chatId,
254
+ status: envelope.snapshot.data.runtime.status,
255
+ messageCount: envelope.snapshot.data.messages.length,
256
+ })
257
+ }
208
258
  send(ws, envelope)
209
259
  }
210
260
  }
@@ -410,8 +460,19 @@ export function createWsRouter({
410
460
  send(ws, { v: PROTOCOL_VERSION, type: "ack", id })
411
461
  break
412
462
  }
463
+ case "chat.setDraftProtection": {
464
+ ws.data.protectedDraftChatIds = new Set(command.chatIds)
465
+ send(ws, { v: PROTOCOL_VERSION, type: "ack", id })
466
+ break
467
+ }
413
468
  case "chat.send": {
414
469
  const result = await agent.send(command)
470
+ const profile = command.clientTraceId && result.chatId
471
+ ? agent.getActiveTurnProfile(result.chatId)
472
+ : null
473
+ logSendToStartingProfile(profile?.traceId ?? command.clientTraceId, profile?.startedAt, "ws.chat_send_ack", {
474
+ chatId: result.chatId ?? null,
475
+ })
415
476
  send(ws, { v: PROTOCOL_VERSION, type: "ack", id, result })
416
477
  break
417
478
  }
@@ -67,11 +67,13 @@ export type ClientCommand =
67
67
  | { type: "chat.create"; projectId: string }
68
68
  | { type: "chat.rename"; chatId: string; title: string }
69
69
  | { type: "chat.delete"; chatId: string }
70
+ | { type: "chat.setDraftProtection"; chatIds: string[] }
70
71
  | { type: "chat.markRead"; chatId: string }
71
72
  | {
72
73
  type: "chat.send"
73
74
  chatId?: string
74
75
  projectId?: string
76
+ clientTraceId?: string
75
77
  provider?: AgentProvider
76
78
  content: string
77
79
  attachments?: ChatAttachment[]