@cuongtran001/kanna 1.76.2 → 1.76.4

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 (96) hide show
  1. package/dist/client/assets/{BoardsPage-BvYUo0PD.js → BoardsPage-e9KN-PqL.js} +1 -1
  2. package/dist/client/assets/{BoardsRoutePage-D5hLNJ2q.js → BoardsRoutePage-BfAUEg94.js} +1 -1
  3. package/dist/client/assets/ChatPage-CoLgIycl.js +93 -0
  4. package/dist/client/assets/{CronJobRow-VCv0jyI2.js → CronJobRow-R6sGCVRD.js} +1 -1
  5. package/dist/client/assets/{CronJobsPage-8FE4cO2q.js → CronJobsPage-DK-B2TNe.js} +1 -1
  6. package/dist/client/assets/{FilePreviewSheet-B5b8L-4e.js → FilePreviewSheet-O3nyFK2i.js} +2 -2
  7. package/dist/client/assets/{FlowSurface-DmNZQO7Z.js → FlowSurface-De4fzKB9.js} +1 -1
  8. package/dist/client/assets/{GenUIBlock-DX1nLbtf.js → GenUIBlock-D6oiFJaL.js} +2 -2
  9. package/dist/client/assets/{KannaSocketProvider-CiuKyRYE.js → KannaSocketProvider-BHItG6ob.js} +1 -1
  10. package/dist/client/assets/{LocalProjectsPage-DjCZmQ8c.js → LocalProjectsPage-C4YvkutW.js} +1 -1
  11. package/dist/client/assets/SettingsPage-DuDvIWI7.js +13 -0
  12. package/dist/client/assets/{StackBoardsRoutePage-i7WMqsk0.js → StackBoardsRoutePage-CEaF8HQA.js} +1 -1
  13. package/dist/client/assets/{ToolCallMessage-BhxowUjs.js → ToolCallMessage-CCR_uV98.js} +1 -1
  14. package/dist/client/assets/{VChartSurface-DQ6HIQXe.js → VChartSurface-JKYw0D-r.js} +1 -1
  15. package/dist/client/assets/{WorkflowsPage-BCIsFa6f.js → WorkflowsPage-B1ZvlJZE.js} +1 -1
  16. package/dist/client/assets/{WorkflowsSection-B5TkVqRA.js → WorkflowsSection-CCRo-MxO.js} +1 -1
  17. package/dist/client/assets/{chart-renderer-DQLdORyQ.js → chart-renderer-BM8heEao.js} +1 -1
  18. package/dist/client/assets/{chatSounds-CUN4vZG0.js → chatSounds-BmmQ91hl.js} +1 -1
  19. package/dist/client/assets/chevron-left-yjfH1ZSB.js +1 -0
  20. package/dist/client/assets/{clipboard.adapter-BvW6oQa_.js → clipboard.adapter-CJ_H99S1.js} +1 -1
  21. package/dist/client/assets/{columnStyle-C0IvZxLY.js → columnStyle-Dn_3F527.js} +1 -1
  22. package/dist/client/assets/{contextWindow-WJUHfP_d.js → contextWindow-BA0xOQMO.js} +1 -1
  23. package/dist/client/assets/copyFeedbackStore-CUQsds64.js +1 -0
  24. package/dist/client/assets/{createScopedStore-DW4wfjbM.js → createScopedStore-DqP7mPc0.js} +1 -1
  25. package/dist/client/assets/{dist-Q8elClTa.js → dist-ChIeJWx6.js} +1 -1
  26. package/dist/client/assets/{ellipsis-D_1qLZbX.js → ellipsis-hKiqLkY4.js} +1 -1
  27. package/dist/client/assets/{flow-kinds-Df0GmP61.js → flow-kinds-C9nOfZHH.js} +1 -1
  28. package/dist/client/assets/{folder-UHukK2MD.js → folder-BiZuYd4N.js} +1 -1
  29. package/dist/client/assets/{formatDuration-BA1K5CFs.js → formatDuration-C6zrNbeh.js} +1 -1
  30. package/dist/client/assets/{genui-CZI72r1O.js → genui-CnB2lRaB.js} +1 -1
  31. package/dist/client/assets/{globe-CQI7GjsK.js → globe-D4_HmNVj.js} +1 -1
  32. package/dist/client/assets/{host-C0p_56Re.js → host-B3gUJGiJ.js} +1 -1
  33. package/dist/client/assets/index-BYus2o1t.css +1 -0
  34. package/dist/client/assets/{index-3lHvRekg.js → index-uQsXmgHP.js} +3 -3
  35. package/dist/client/assets/{info-BXzVPePc.js → info-B_Vqy-5o.js} +1 -1
  36. package/dist/client/assets/{input-BerPPEIs.js → input-DRve70PS.js} +1 -1
  37. package/dist/client/assets/{lexicalToReact-DdXqoi0d.js → lexicalToReact-BFuz5nt-.js} +3 -3
  38. package/dist/client/assets/{log-7fGlFSON.js → log-DWUXpF9D.js} +1 -1
  39. package/dist/client/assets/{monitor-BOFtyQiW.js → monitor-DDe-a8Wg.js} +1 -1
  40. package/dist/client/assets/{pendingActionsStore-idc6m2Ya.js → pendingActionsStore-in-8OIop.js} +1 -1
  41. package/dist/client/assets/{play-DltDjX9H.js → play-CCpwuwmW.js} +1 -1
  42. package/dist/client/assets/{plus-CwWMJ0a-.js → plus-DF1Xx1dC.js} +1 -1
  43. package/dist/client/assets/{popover-gn1uvc-1.js → popover-CuSv5Wru.js} +1 -1
  44. package/dist/client/assets/{pushClient-NeXrldi-.js → pushClient-DtF2D2cW.js} +1 -1
  45. package/dist/client/assets/runDetached-B5RMTcFn.js +1 -0
  46. package/dist/client/assets/{segmented-control-3UDSwswP.js → segmented-control-2bnq74MU.js} +1 -1
  47. package/dist/client/assets/{select-Df67vheJ.js → select-BZMQ2erm.js} +1 -1
  48. package/dist/client/assets/{settings-header-button-CKZHCdXh.js → settings-header-button-Df7-MZFW.js} +1 -1
  49. package/dist/client/assets/{sidebarPendingActions-B8iNQJfE.js → sidebarPendingActions-DPIIvMHh.js} +1 -1
  50. package/dist/client/assets/{square-pen-DJjPW4f4.js → square-pen-CKf_gOOo.js} +1 -1
  51. package/dist/client/assets/{state-mark-CU2hw5D9.js → state-mark-DVh0A9N6.js} +1 -1
  52. package/dist/client/assets/{terminal-8Bxl5uZh.js → terminal-CHsxiDwH.js} +1 -1
  53. package/dist/client/assets/{timer.adapter-BD7zwXEm.js → timer.adapter-Besjjb6B.js} +1 -1
  54. package/dist/client/assets/{tools-Lo2VrFmM.js → tools-B1sdThgL.js} +1 -1
  55. package/dist/client/assets/{triangle-alert-CQwDyl4K.js → triangle-alert-DlaH1f-p.js} +1 -1
  56. package/dist/client/assets/{useAppGlobalState-uP5C1BG8.js → useAppGlobalState-cMGGvgsS.js} +1 -1
  57. package/dist/client/assets/{useKannaState-B2eDC-Dw.js → useKannaState-gc-Chi9u.js} +1 -1
  58. package/dist/client/assets/{useTheme-B_JDh31l.js → useTheme-B1rO5zPx.js} +1 -1
  59. package/dist/client/index.html +34 -34
  60. package/package.json +1 -1
  61. package/src/server/agent-coordinator-types.ts +1 -0
  62. package/src/server/agent-coordinator.ts +4 -0
  63. package/src/server/beacon-mention-prompt.test.ts +33 -0
  64. package/src/server/beacon-mention-prompt.ts +33 -0
  65. package/src/server/beacon-registry.test.ts +1 -0
  66. package/src/server/beacon-registry.ts +9 -1
  67. package/src/server/beacon-services.test.ts +11 -1
  68. package/src/server/beacon-services.ts +4 -0
  69. package/src/server/beacon-settings.test.ts +74 -1
  70. package/src/server/beacon-settings.ts +54 -3
  71. package/src/server/claude-turn-starter-types.ts +3 -0
  72. package/src/server/claude-turn-starter.test.ts +85 -0
  73. package/src/server/claude-turn-starter.ts +5 -1
  74. package/src/server/kanna-mcp-beacon-transfer.test.ts +1 -0
  75. package/src/server/kanna-mcp-beacon.test.ts +4 -1
  76. package/src/server/kanna-mcp-beacon.ts +16 -5
  77. package/src/server/kanna-mcp-tools/chat-tasks.ts +1 -1
  78. package/src/server/push/push-manager.test.ts +39 -0
  79. package/src/server/push/push-manager.ts +45 -1
  80. package/src/server/server.ts +1 -0
  81. package/src/server/ws-router-broadcast.ts +1 -1
  82. package/src/server/ws-router-envelope.ts +2 -0
  83. package/src/server/ws-router-settings.test.ts +1 -0
  84. package/src/server/ws-router.test.ts +68 -0
  85. package/src/shared/beacon-alias.test.ts +64 -0
  86. package/src/shared/beacon-alias.ts +120 -0
  87. package/src/shared/beacon-config.ts +2 -0
  88. package/src/shared/beacon-status.test.ts +2 -0
  89. package/src/shared/beacon-status.ts +2 -0
  90. package/src/shared/chat-activity.ts +8 -0
  91. package/dist/client/assets/ChatPage-zfy8nznW.js +0 -93
  92. package/dist/client/assets/SettingsPage-ELjbPQEw.js +0 -13
  93. package/dist/client/assets/chevron-left-DpI8tMxJ.js +0 -1
  94. package/dist/client/assets/copyFeedbackStore-BsjXKW0-.js +0 -1
  95. package/dist/client/assets/index-D8o_YX_z.css +0 -1
  96. package/dist/client/assets/runDetached-BWsF_7Xo.js +0 -1
@@ -0,0 +1,64 @@
1
+ import { describe, expect, test } from "bun:test"
2
+ import {
3
+ beaconDisplayName,
4
+ buildBeaconMentionIndex,
5
+ resolveBeaconMentions,
6
+ validateBeaconAliasFormat,
7
+ } from "./beacon-alias"
8
+ import type { BeaconConfig } from "./beacon-config"
9
+ import { DEFAULT_BEACON_SCOPE } from "./beacon-scope"
10
+
11
+ function config(id: string, label: string, alias?: string): BeaconConfig {
12
+ return {
13
+ id,
14
+ label,
15
+ ...(alias === undefined ? {} : { alias }),
16
+ publicKey: "pk",
17
+ os: "linux",
18
+ scope: DEFAULT_BEACON_SCOPE,
19
+ enabled: true,
20
+ createdAt: "2026-10-13T00:00:00.000Z",
21
+ updatedAt: "2026-10-13T00:00:00.000Z",
22
+ }
23
+ }
24
+
25
+ describe("beacon alias", () => {
26
+ test("the display name prefers the alias over the hostname label", () => {
27
+ expect(beaconDisplayName({ alias: "office-mac", label: "MacBook-Pro.local" })).toBe("office-mac")
28
+ expect(beaconDisplayName({ alias: null, label: "MacBook-Pro.local" })).toBe("MacBook-Pro.local")
29
+ })
30
+
31
+ test("format accepts a slug handle and rejects uppercase, spaces and an overlong name", () => {
32
+ expect(validateBeaconAliasFormat("office-mac_2")).toBeNull()
33
+ expect(validateBeaconAliasFormat("Office")).not.toBeNull()
34
+ expect(validateBeaconAliasFormat("my mac")).not.toBeNull()
35
+ expect(validateBeaconAliasFormat("a".repeat(41))).not.toBeNull()
36
+ })
37
+ })
38
+
39
+ describe("beacon mention index", () => {
40
+ test("an alias claims its handle before another beacon's hostname slug", () => {
41
+ const index = buildBeaconMentionIndex([config("a", "office-mac"), config("z", "other", "office-mac")])
42
+ expect(index.beaconIdByHandle.get("office-mac")).toBe("z")
43
+ expect(index.handleByBeaconId.get("a")).toBe("office-mac-2")
44
+ })
45
+
46
+ test("a beacon with no alias is addressed by a slug of its hostname", () => {
47
+ const index = buildBeaconMentionIndex([config("a", "MacBook Pro.local")])
48
+ expect(index.handleByBeaconId.get("a")).toBe("macbook-pro-local")
49
+ })
50
+
51
+ test("colliding hostname slugs are suffixed in id order regardless of input order", () => {
52
+ const forward = buildBeaconMentionIndex([config("a", "Build Box"), config("b", "build-box")])
53
+ const backward = buildBeaconMentionIndex([config("b", "build-box"), config("a", "Build Box")])
54
+ expect([...forward.handleByBeaconId]).toEqual([["a", "build-box"], ["b", "build-box-2"]])
55
+ expect([...backward.handleByBeaconId]).toEqual([...forward.handleByBeaconId])
56
+ })
57
+
58
+ test("a message mention resolves to the beacon and an unknown handle is reported unresolved", () => {
59
+ const beacons = [config("a", "laptop", "office-mac")]
60
+ const resolved = resolveBeaconMentions("run uname on @beacon/office-mac and @beacon/ghost", beacons)
61
+ expect(resolved.beacons.map((beacon) => beacon.id)).toEqual(["a"])
62
+ expect(resolved.unresolvedHandles).toEqual(["ghost"])
63
+ })
64
+ })
@@ -0,0 +1,120 @@
1
+ import type { BeaconConfig } from "./beacon-config"
2
+
3
+ export const BEACON_ALIAS_PATTERN = /^[a-z0-9][a-z0-9_-]{0,39}$/
4
+
5
+ export const BEACON_ALIAS_MAX_LENGTH = 40
6
+
7
+ export const BEACON_MENTION_PREFIX = "beacon/"
8
+
9
+ export const BEACON_MENTION_PATTERN = "(^|[\\s\\n\\t])@beacon/([A-Za-z0-9][A-Za-z0-9_-]*)"
10
+
11
+ export function createBeaconMentionRegex(): RegExp {
12
+ return new RegExp(BEACON_MENTION_PATTERN, "g")
13
+ }
14
+
15
+ export function validateBeaconAliasFormat(alias: string): string | null {
16
+ if (alias.length === 0) return "Short name is required"
17
+ if (alias.length > BEACON_ALIAS_MAX_LENGTH) {
18
+ return `Short name must be at most ${BEACON_ALIAS_MAX_LENGTH} characters`
19
+ }
20
+ if (!BEACON_ALIAS_PATTERN.test(alias)) {
21
+ return "Use lowercase letters, digits, - and _, starting with a letter or digit"
22
+ }
23
+ return null
24
+ }
25
+
26
+ export function beaconDisplayName(beacon: { alias?: string | null; label: string }): string {
27
+ return beacon.alias ?? beacon.label
28
+ }
29
+
30
+ export interface MentionableBeacon {
31
+ id: string
32
+ label: string
33
+ alias?: string | null
34
+ }
35
+
36
+ export interface BeaconMentionIndex {
37
+ handleByBeaconId: ReadonlyMap<string, string>
38
+ beaconIdByHandle: ReadonlyMap<string, string>
39
+ }
40
+
41
+ const INVALID_HANDLE_CHARS = /[^a-z0-9_-]+/g
42
+ const EDGE_SEPARATORS = /^[-_]+|[-_]+$/g
43
+
44
+ function slugifyLabel(label: string): string {
45
+ const slug = label
46
+ .toLowerCase()
47
+ .replace(INVALID_HANDLE_CHARS, "-")
48
+ .replace(EDGE_SEPARATORS, "")
49
+ .slice(0, BEACON_ALIAS_MAX_LENGTH)
50
+ .replace(EDGE_SEPARATORS, "")
51
+ return slug.length > 0 ? slug : "beacon"
52
+ }
53
+
54
+ function freeHandle(base: string, taken: ReadonlyMap<string, string>): string {
55
+ if (!taken.has(base)) return base
56
+ let suffix = 2
57
+ while (taken.has(`${base}-${suffix}`)) suffix += 1
58
+ return `${base}-${suffix}`
59
+ }
60
+
61
+ export function buildBeaconMentionIndex(beacons: readonly MentionableBeacon[]): BeaconMentionIndex {
62
+ const ordered = [...beacons].sort((left, right) => left.id.localeCompare(right.id))
63
+ const handleByBeaconId = new Map<string, string>()
64
+ const beaconIdByHandle = new Map<string, string>()
65
+
66
+ for (const beacon of ordered) {
67
+ if (beacon.alias === undefined || beacon.alias === null) continue
68
+ if (beaconIdByHandle.has(beacon.alias)) continue
69
+ handleByBeaconId.set(beacon.id, beacon.alias)
70
+ beaconIdByHandle.set(beacon.alias, beacon.id)
71
+ }
72
+
73
+ for (const beacon of ordered) {
74
+ if (handleByBeaconId.has(beacon.id)) continue
75
+ const handle = freeHandle(slugifyLabel(beacon.label), beaconIdByHandle)
76
+ handleByBeaconId.set(beacon.id, handle)
77
+ beaconIdByHandle.set(handle, beacon.id)
78
+ }
79
+
80
+ return { handleByBeaconId, beaconIdByHandle }
81
+ }
82
+
83
+ export function parseBeaconMentionHandles(text: string): string[] {
84
+ const handles: string[] = []
85
+ for (const match of text.matchAll(createBeaconMentionRegex())) {
86
+ const handle = match[2]
87
+ if (handle !== undefined) handles.push(handle.toLowerCase())
88
+ }
89
+ return handles
90
+ }
91
+
92
+ export interface BeaconMentionResolution {
93
+ beacons: BeaconConfig[]
94
+ unresolvedHandles: string[]
95
+ }
96
+
97
+ export function resolveBeaconMentions(
98
+ text: string,
99
+ beacons: readonly BeaconConfig[],
100
+ ): BeaconMentionResolution {
101
+ const handles = parseBeaconMentionHandles(text)
102
+ if (handles.length === 0) return { beacons: [], unresolvedHandles: [] }
103
+ const { beaconIdByHandle } = buildBeaconMentionIndex(beacons)
104
+ const byId = new Map(beacons.map((beacon) => [beacon.id, beacon]))
105
+ const resolved: BeaconConfig[] = []
106
+ const unresolvedHandles: string[] = []
107
+ const seen = new Set<string>()
108
+ for (const handle of handles) {
109
+ const id = beaconIdByHandle.get(handle)
110
+ const beacon = id === undefined ? undefined : byId.get(id)
111
+ if (beacon === undefined) {
112
+ if (!unresolvedHandles.includes(handle)) unresolvedHandles.push(handle)
113
+ continue
114
+ }
115
+ if (seen.has(beacon.id)) continue
116
+ seen.add(beacon.id)
117
+ resolved.push(beacon)
118
+ }
119
+ return { beacons: resolved, unresolvedHandles }
120
+ }
@@ -4,6 +4,7 @@ import type { BeaconScope } from "./beacon-scope"
4
4
  export interface BeaconConfig {
5
5
  id: string
6
6
  label: string
7
+ alias?: string
7
8
  publicKey: string
8
9
  os: BeaconOs
9
10
  scope: BeaconScope
@@ -20,6 +21,7 @@ export interface BeaconInput {
20
21
 
21
22
  export interface BeaconPatch {
22
23
  label?: string
24
+ alias?: string | null
23
25
  scope?: BeaconScope
24
26
  enabled?: boolean
25
27
  }
@@ -28,6 +28,7 @@ describe("buildBeaconStatusRows", () => {
28
28
  {
29
29
  id: "a",
30
30
  label: "label-a",
31
+ alias: null,
31
32
  os: "linux",
32
33
  enabled: true,
33
34
  online: true,
@@ -39,6 +40,7 @@ describe("buildBeaconStatusRows", () => {
39
40
  {
40
41
  id: "b",
41
42
  label: "label-b",
43
+ alias: null,
42
44
  os: "windows",
43
45
  enabled: false,
44
46
  online: false,
@@ -4,6 +4,7 @@ import type { BeaconConfig } from "./beacon-config"
4
4
  export interface BeaconStatusRow {
5
5
  id: string
6
6
  label: string
7
+ alias: string | null
7
8
  os: BeaconOs
8
9
  enabled: boolean
9
10
  online: boolean
@@ -37,6 +38,7 @@ export function buildBeaconStatusRows(
37
38
  return {
38
39
  id: config.id,
39
40
  label: config.label,
41
+ alias: config.alias ?? null,
40
42
  os: config.os,
41
43
  enabled: config.enabled,
42
44
  online: state?.online ?? false,
@@ -0,0 +1,8 @@
1
+ import type { ChatActivity } from "./types"
2
+
3
+ export function chatHasPendingWork(activity: ChatActivity): boolean {
4
+ return activity.backgroundTasks > 0
5
+ || activity.agents > 0
6
+ || activity.workflow !== null
7
+ || activity.loop !== null
8
+ }