snow-flow 10.0.186-dev.682 → 10.0.186-dev.683

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
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "10.0.186-dev.682",
3
+ "version": "10.0.186-dev.683",
4
4
  "name": "snow-flow",
5
5
  "description": "Snow-Flow - ServiceNow Multi-Agent Development Framework powered by AI",
6
6
  "license": "Elastic-2.0",
@@ -7,6 +7,7 @@ import {
7
7
  For,
8
8
  Match,
9
9
  on,
10
+ onCleanup,
10
11
  onMount,
11
12
  Show,
12
13
  Switch,
@@ -202,7 +203,7 @@ export function Session() {
202
203
  })
203
204
 
204
205
  let lastSwitch: string | undefined = undefined
205
- sdk.event.on("message.part.updated", (evt) => {
206
+ const unsubPartUpdated = sdk.event.on("message.part.updated", (evt) => {
206
207
  const part = evt.properties.part
207
208
  if (part.type !== "tool") return
208
209
  if (part.sessionID !== route.sessionID) return
@@ -217,6 +218,7 @@ export function Session() {
217
218
  lastSwitch = part.id
218
219
  }
219
220
  })
221
+ onCleanup(() => unsubPartUpdated())
220
222
 
221
223
  let scroll: ScrollBoxRenderable
222
224
  let prompt: PromptRef
@@ -100,9 +100,11 @@ export namespace Format {
100
100
  return result
101
101
  }
102
102
 
103
+ let formatUnsub: (() => void) | undefined
103
104
  export function init() {
104
105
  log.info("init")
105
- Bus.subscribe(File.Event.Edited, async (payload) => {
106
+ formatUnsub?.()
107
+ formatUnsub = Bus.subscribe(File.Event.Edited, async (payload) => {
106
108
  const file = payload.properties.file
107
109
  log.info("formatting", { file })
108
110
  const ext = path.extname(file)
@@ -116,6 +116,7 @@ export namespace Plugin {
116
116
  return state().then((x) => x.hooks)
117
117
  }
118
118
 
119
+ let busUnsub: (() => void) | undefined
119
120
  export async function init() {
120
121
  const hooks = await state().then((x) => x.hooks)
121
122
  const config = await Config.get()
@@ -123,7 +124,9 @@ export namespace Plugin {
123
124
  // @ts-expect-error this is because we haven't moved plugin to sdk v2
124
125
  await hook.config?.(config)
125
126
  }
126
- Bus.subscribeAll(async (input) => {
127
+ // Clean up previous subscription to prevent accumulation on re-init
128
+ busUnsub?.()
129
+ busUnsub = Bus.subscribeAll(async (input) => {
127
130
  const hooks = await state().then((x) => x.hooks)
128
131
  for (const hook of hooks) {
129
132
  hook["event"]?.({
@@ -75,9 +75,11 @@ export async function InstanceBootstrap() {
75
75
  await ensureAgentsMd()
76
76
  await ensureInstanceMd()
77
77
 
78
- Bus.subscribe(Command.Event.Executed, async (payload) => {
78
+ bootstrapUnsub?.()
79
+ bootstrapUnsub = Bus.subscribe(Command.Event.Executed, async (payload) => {
79
80
  if (payload.properties.name === Command.Default.INIT) {
80
81
  await Project.setInitialized(Instance.project.id)
81
82
  }
82
83
  })
83
84
  }
85
+ let bootstrapUnsub: (() => void) | undefined
@@ -447,6 +447,8 @@ export namespace MessageV2 {
447
447
 
448
448
  // In-memory message cache: avoids re-reading all messages from disk on every loop iteration.
449
449
  // Uses Instance.state for per-instance isolation and Bus events for cache invalidation.
450
+ // LRU eviction: only the most recently accessed sessions are kept in memory.
451
+ const MAX_CACHED_SESSIONS = 3
450
452
  const messageCache = Instance.state(
451
453
  () => {
452
454
  const sessions = new Map<string, MessageV2.WithParts[]>()
@@ -508,7 +510,12 @@ export namespace MessageV2 {
508
510
  export async function streamCached(sessionID: string): Promise<WithParts[]> {
509
511
  const { sessions } = messageCache()
510
512
  const existing = sessions.get(sessionID)
511
- if (existing) return existing
513
+ if (existing) {
514
+ // LRU: move to end (most recently used)
515
+ sessions.delete(sessionID)
516
+ sessions.set(sessionID, existing)
517
+ return existing
518
+ }
512
519
 
513
520
  // Cold start: load from disk and populate cache
514
521
  const result: WithParts[] = []
@@ -517,6 +524,13 @@ export namespace MessageV2 {
517
524
  }
518
525
  result.reverse() // stream yields newest first, we want chronological order
519
526
  sessions.set(sessionID, result)
527
+
528
+ // LRU eviction: remove oldest sessions if over limit
529
+ while (sessions.size > MAX_CACHED_SESSIONS) {
530
+ const oldest = sessions.keys().next().value
531
+ if (oldest) sessions.delete(oldest)
532
+ }
533
+
520
534
  return result
521
535
  }
522
536
 
@@ -17,52 +17,57 @@ export namespace ShareNext {
17
17
 
18
18
  const disabled = process.env["OPENCODE_DISABLE_SHARE"] === "true" || process.env["OPENCODE_DISABLE_SHARE"] === "1"
19
19
 
20
+ const shareNextUnsubs: (() => void)[] = []
20
21
  export async function init() {
21
22
  if (disabled) return
22
- Bus.subscribe(Session.Event.Updated, async (evt) => {
23
- await sync(evt.properties.info.id, [
24
- {
25
- type: "session",
26
- data: evt.properties.info,
27
- },
28
- ])
29
- })
30
- Bus.subscribe(MessageV2.Event.Updated, async (evt) => {
31
- await sync(evt.properties.info.sessionID, [
32
- {
33
- type: "message",
34
- data: evt.properties.info as any,
35
- },
36
- ])
37
- if (evt.properties.info.role === "user") {
23
+ for (const unsub of shareNextUnsubs) unsub()
24
+ shareNextUnsubs.length = 0
25
+ shareNextUnsubs.push(
26
+ Bus.subscribe(Session.Event.Updated, async (evt) => {
27
+ await sync(evt.properties.info.id, [
28
+ {
29
+ type: "session",
30
+ data: evt.properties.info,
31
+ },
32
+ ])
33
+ }),
34
+ Bus.subscribe(MessageV2.Event.Updated, async (evt) => {
38
35
  await sync(evt.properties.info.sessionID, [
39
36
  {
40
- type: "model",
41
- data: [
42
- await Provider.getModel(evt.properties.info.model.providerID, evt.properties.info.model.modelID).then(
43
- (m) => m,
44
- ),
45
- ],
37
+ type: "message",
38
+ data: evt.properties.info as any,
46
39
  },
47
40
  ])
48
- }
49
- })
50
- Bus.subscribe(MessageV2.Event.PartUpdated, async (evt) => {
51
- await sync(evt.properties.part.sessionID, [
52
- {
53
- type: "part",
54
- data: evt.properties.part,
55
- },
56
- ])
57
- })
58
- Bus.subscribe(Session.Event.Diff, async (evt) => {
59
- await sync(evt.properties.sessionID, [
60
- {
61
- type: "session_diff",
62
- data: evt.properties.diff,
63
- },
64
- ])
65
- })
41
+ if (evt.properties.info.role === "user") {
42
+ await sync(evt.properties.info.sessionID, [
43
+ {
44
+ type: "model",
45
+ data: [
46
+ await Provider.getModel(evt.properties.info.model.providerID, evt.properties.info.model.modelID).then(
47
+ (m) => m,
48
+ ),
49
+ ],
50
+ },
51
+ ])
52
+ }
53
+ }),
54
+ Bus.subscribe(MessageV2.Event.PartUpdated, async (evt) => {
55
+ await sync(evt.properties.part.sessionID, [
56
+ {
57
+ type: "part",
58
+ data: evt.properties.part,
59
+ },
60
+ ])
61
+ }),
62
+ Bus.subscribe(Session.Event.Diff, async (evt) => {
63
+ await sync(evt.properties.sessionID, [
64
+ {
65
+ type: "session_diff",
66
+ data: evt.properties.diff,
67
+ },
68
+ ])
69
+ }),
70
+ )
66
71
  }
67
72
 
68
73
  export async function create(sessionID: string) {
@@ -46,24 +46,29 @@ export namespace Share {
46
46
  })
47
47
  }
48
48
 
49
+ const shareUnsubs: (() => void)[] = []
49
50
  export function init() {
50
- Bus.subscribe(Session.Event.Updated, async (evt) => {
51
- await sync("session/info/" + evt.properties.info.id, evt.properties.info)
52
- })
53
- Bus.subscribe(MessageV2.Event.Updated, async (evt) => {
54
- await sync("session/message/" + evt.properties.info.sessionID + "/" + evt.properties.info.id, evt.properties.info)
55
- })
56
- Bus.subscribe(MessageV2.Event.PartUpdated, async (evt) => {
57
- await sync(
58
- "session/part/" +
59
- evt.properties.part.sessionID +
60
- "/" +
61
- evt.properties.part.messageID +
62
- "/" +
63
- evt.properties.part.id,
64
- evt.properties.part,
65
- )
66
- })
51
+ for (const unsub of shareUnsubs) unsub()
52
+ shareUnsubs.length = 0
53
+ shareUnsubs.push(
54
+ Bus.subscribe(Session.Event.Updated, async (evt) => {
55
+ await sync("session/info/" + evt.properties.info.id, evt.properties.info)
56
+ }),
57
+ Bus.subscribe(MessageV2.Event.Updated, async (evt) => {
58
+ await sync("session/message/" + evt.properties.info.sessionID + "/" + evt.properties.info.id, evt.properties.info)
59
+ }),
60
+ Bus.subscribe(MessageV2.Event.PartUpdated, async (evt) => {
61
+ await sync(
62
+ "session/part/" +
63
+ evt.properties.part.sessionID +
64
+ "/" +
65
+ evt.properties.part.messageID +
66
+ "/" +
67
+ evt.properties.part.id,
68
+ evt.properties.part,
69
+ )
70
+ }),
71
+ )
67
72
  }
68
73
 
69
74
  export const URL =