@cuongtran001/kanna 1.3.0 → 1.4.0
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/dist/client/assets/{arc-CAysfmX5.js → arc-BHj370W7.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-BKqRYsm4.js → architectureDiagram-3BPJPVTR-1G88q1PY.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-Ce7Eorec.js → blockDiagram-GPEHLZMM-D0L3Co5v.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-CJ77zauH.js → c4Diagram-AAUBKEIU-CQtERl3H.js} +1 -1
- package/dist/client/assets/channel-BP9IMEvd.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-Rk_3zLxJ.js → chunk-2J33WTMH-DxOwt09f.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-DPIz6Nvf.js → chunk-4BX2VUAB-Wry66y9R.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-D-TkbGh2.js → chunk-55IACEB6-D6p-Hi4x.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-B4NOSIZV.js → chunk-727SXJPM-CRQZt23N.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-Cm_TGLYm.js → chunk-AQP2D5EJ-BzgTKOM9.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-Bu45BUVh.js → chunk-FMBD7UC4-CKfD-91C.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-C1Bw8_gg.js → chunk-ND2GUHAM-a71B27wc.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-DueG15o0.js → chunk-QZHKN3VN-vv9gJwx0.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK--W6Wt7FF.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2--W6Wt7FF.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-YgpMurXf.js → cose-bilkent-S5V4N54A-Cu-dCCaI.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-Dop7Oq67.js → dagre-BM42HDAG-D8coOWLK.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-unMfBEJH.js → diagram-2AECGRRQ-NIwZ65jE.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-D9EJ9qMI.js → diagram-5GNKFQAL-d46aE_le.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-C5IykOIR.js → diagram-KO2AKTUF-xdm2UxWc.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-DJAuPngz.js → diagram-LMA3HP47-hDbLwC5K.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-B3wHFtS-.js → diagram-OG6HWLK6-MHUGG7ki.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-Dm7kt9o5.js → erDiagram-TEJ5UH35-Ro6n9X70.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-s9Lds3ny.js → flowDiagram-I6XJVG4X-DOZqvRMu.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-CRQNYdAf.js → ganttDiagram-6RSMTGT7-Bn2NXYIy.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-DhYjK4Jb.js → gitGraphDiagram-PVQCEYII-DK44bfbC.js} +1 -1
- package/dist/client/assets/{index-C0U2kwuB.js → index-CWJpHyQy.js} +115 -115
- package/dist/client/assets/{index-CLsOWXff.css → index-Cyzd4zZx.css} +1 -1
- package/dist/client/assets/{index-CbSY9HfS.js → index-DU56d4oC.js} +1 -1
- package/dist/client/assets/{infoDiagram-5YYISTIA-CRKJ0PSH.js → infoDiagram-5YYISTIA-C2tW_Dwj.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-CEAkB0IY.js → ishikawaDiagram-YF4QCWOH-C__4Z1wz.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-CyOK5_DG.js → journeyDiagram-JHISSGLW-Bbn5EawR.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-BfKjiJSo.js → kanban-definition-UN3LZRKU-RgS5IRWl.js} +1 -1
- package/dist/client/assets/{linear-DLR6miy3.js → linear-DfI0qBO_.js} +1 -1
- package/dist/client/assets/{mermaid.core-D7AdC_lE.js → mermaid.core-BiN10VHc.js} +4 -4
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-zYyJtDSa.js → mindmap-definition-RKZ34NQL-BQhibhh3.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-BgP8PFlo.js → pieDiagram-4H26LBE5-BEfLRn7a.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-Di8h7J96.js → quadrantDiagram-W4KKPZXB-ba641Y2x.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-DuTzFl3R.js → requirementDiagram-4Y6WPE33-BGtl-tVO.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-DBRoMm_T.js → sankeyDiagram-5OEKKPKP-DyziaSFr.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-Bmb-1HUN.js → sequenceDiagram-3UESZ5HK-BfYBAYga.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-fnv5ayn-.js → stateDiagram-AJRCARHV-BfUfmLYA.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CdGcgYEq.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-ByWRdpQX.js → timeline-definition-PNZ67QCA-T6z7dEFl.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN--2WGpVXW.js → vennDiagram-CIIHVFJN-BAwQbFFd.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-24zlnzxK.js → wardley-L42UT6IY-DF1cRKsP.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-Cllvqb3K.js → wardleyDiagram-YWT4CUSO-BRtAZ8za.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-ZcdC6nPE.js → xychartDiagram-2RQKCTM6-Brb-q76h.js} +1 -1
- package/dist/client/index.html +2 -2
- package/package.json +1 -1
- package/src/server/agent-coordinator-types.ts +158 -0
- package/src/server/agent-coordinator.ts +919 -0
- package/src/server/agent-deps-builders.ts +425 -0
- package/src/server/agent.test.ts +22 -6
- package/src/server/agent.ts +35 -4987
- package/src/server/chat-op-log.test.ts +63 -0
- package/src/server/chat-op-log.ts +71 -0
- package/src/server/chat-ops-diff.test.ts +120 -0
- package/src/server/chat-ops-diff.ts +97 -0
- package/src/server/chat-ops-parity.test.ts +140 -0
- package/src/server/claude-autocontinue-commands.test.ts +494 -0
- package/src/server/claude-autocontinue-commands.ts +256 -0
- package/src/server/claude-cancel-handler.test.ts +472 -0
- package/src/server/claude-cancel-handler.ts +241 -0
- package/src/server/claude-chat-management.test.ts +395 -0
- package/src/server/claude-chat-management.ts +251 -0
- package/src/server/claude-harness-stream.test.ts +273 -0
- package/src/server/claude-harness-stream.ts +158 -0
- package/src/server/claude-loop-orch-commands.test.ts +579 -0
- package/src/server/claude-loop-orch-commands.ts +466 -0
- package/src/server/claude-message-normalizer.test.ts +213 -0
- package/src/server/claude-message-normalizer.ts +436 -0
- package/src/server/claude-prompt-helpers.test.ts +248 -0
- package/src/server/claude-prompt-helpers.ts +139 -0
- package/src/server/claude-sdk-queue.test.ts +169 -0
- package/src/server/claude-sdk-queue.ts +82 -0
- package/src/server/claude-send-command.test.ts +414 -0
- package/src/server/claude-send-command.ts +420 -0
- package/src/server/claude-session-config-helpers.test.ts +279 -0
- package/src/server/claude-session-config-helpers.ts +151 -0
- package/src/server/claude-session-config.test.ts +250 -0
- package/src/server/claude-session-config.ts +159 -0
- package/src/server/claude-session-error-handler.test.ts +498 -0
- package/src/server/claude-session-error-handler.ts +361 -0
- package/src/server/claude-session-lifecycle.test.ts +669 -0
- package/src/server/claude-session-lifecycle.ts +283 -0
- package/src/server/claude-session-rebuild.test.ts +224 -0
- package/src/server/claude-session-rebuild.ts +101 -0
- package/src/server/claude-session-runner.test.ts +523 -0
- package/src/server/claude-session-runner.ts +521 -0
- package/src/server/claude-session-spawner.test.ts +401 -0
- package/src/server/claude-session-spawner.ts +401 -0
- package/src/server/claude-session-start.test.ts +205 -0
- package/src/server/claude-session-start.ts +277 -0
- package/src/server/claude-session-state-queries.test.ts +344 -0
- package/src/server/claude-session-state-queries.ts +160 -0
- package/src/server/claude-session-state.ts +104 -0
- package/src/server/claude-slash-commands.test.ts +277 -0
- package/src/server/claude-slash-commands.ts +234 -0
- package/src/server/claude-spawn-helpers.test.ts +221 -0
- package/src/server/claude-spawn-helpers.ts +179 -0
- package/src/server/claude-steer-log.test.ts +243 -0
- package/src/server/claude-steer-log.ts +58 -0
- package/src/server/claude-subagent-tool-response.test.ts +213 -0
- package/src/server/claude-subagent-tool-response.ts +165 -0
- package/src/server/claude-subagent-wiring.test.ts +263 -0
- package/src/server/claude-subagent-wiring.ts +324 -0
- package/src/server/claude-tool-respond.test.ts +321 -0
- package/src/server/claude-tool-respond.ts +142 -0
- package/src/server/claude-turn-runner.test.ts +310 -0
- package/src/server/claude-turn-runner.ts +228 -0
- package/src/server/claude-turn-starter.test.ts +266 -0
- package/src/server/claude-turn-starter.ts +566 -0
- package/src/server/claude-usage-math.test.ts +169 -0
- package/src/server/claude-usage-math.ts +132 -0
- package/src/server/diff-store-branch-ops.adapter.test.ts +332 -0
- package/src/server/diff-store-branch-ops.adapter.ts +602 -0
- package/src/server/diff-store-commit-ops.adapter.test.ts +358 -0
- package/src/server/diff-store-commit-ops.adapter.ts +258 -0
- package/src/server/diff-store-errors.ts +85 -0
- package/src/server/diff-store-file-ops.adapter.ts +212 -0
- package/src/server/diff-store-git-branch.adapter.ts +686 -0
- package/src/server/diff-store-io.adapter.ts +27 -0
- package/src/server/diff-store-parse.test.ts +212 -0
- package/src/server/diff-store-parse.ts +122 -0
- package/src/server/diff-store-state.ts +82 -0
- package/src/server/diff-store.ts +76 -1854
- package/src/server/event-store-apply.test.ts +280 -0
- package/src/server/event-store-apply.ts +132 -0
- package/src/server/event-store-chat-lifecycle.test.ts +408 -0
- package/src/server/event-store-chat-lifecycle.ts +441 -0
- package/src/server/event-store-entity-write.ts +356 -0
- package/src/server/event-store-helpers.test.ts +230 -0
- package/src/server/event-store-helpers.ts +235 -0
- package/src/server/event-store-init.ts +276 -0
- package/src/server/event-store-messages.adapter.test.ts +532 -0
- package/src/server/event-store-messages.adapter.ts +455 -0
- package/src/server/event-store-orch-subscription.ts +124 -0
- package/src/server/event-store-orch.test.ts +462 -0
- package/src/server/event-store-orch.ts +312 -0
- package/src/server/event-store-peripheral-events.adapter.test.ts +254 -0
- package/src/server/event-store-peripheral-events.adapter.ts +105 -0
- package/src/server/event-store-snapshot.test.ts +597 -0
- package/src/server/event-store-snapshot.ts +658 -0
- package/src/server/event-store-subagent.test.ts +408 -0
- package/src/server/event-store-subagent.ts +198 -0
- package/src/server/event-store-tool-requests.test.ts +210 -0
- package/src/server/event-store-tool-requests.ts +131 -0
- package/src/server/event-store-transcript-write.adapter.ts +291 -0
- package/src/server/event-store-write-ops.test.ts +554 -0
- package/src/server/event-store-write-ops.ts +529 -0
- package/src/server/event-store.test.ts +75 -0
- package/src/server/event-store.ts +257 -2285
- package/src/server/events.ts +1 -1
- package/src/server/harness-types.ts +15 -1
- package/src/server/oauth-errors.ts +19 -0
- package/src/server/storage/backend.ts +7 -0
- package/src/server/storage/fs-storage.adapter.ts +21 -1
- package/src/server/storage/in-memory-storage.ts +12 -0
- package/src/server/ws-router-agent-ctrl.test.ts +187 -0
- package/src/server/ws-router-agent-ctrl.ts +137 -0
- package/src/server/ws-router-broadcast.ts +560 -0
- package/src/server/ws-router-chat.test.ts +461 -0
- package/src/server/ws-router-chat.ts +279 -0
- package/src/server/ws-router-defaults.test.ts +283 -0
- package/src/server/ws-router-defaults.ts +434 -0
- package/src/server/ws-router-diff.test.ts +167 -0
- package/src/server/ws-router-diff.ts +250 -0
- package/src/server/ws-router-envelope.ts +360 -0
- package/src/server/ws-router-misc.test.ts +422 -0
- package/src/server/ws-router-misc.ts +265 -0
- package/src/server/ws-router-orch.test.ts +190 -0
- package/src/server/ws-router-orch.ts +93 -0
- package/src/server/ws-router-project.test.ts +403 -0
- package/src/server/ws-router-project.ts +256 -0
- package/src/server/ws-router-push.test.ts +193 -0
- package/src/server/ws-router-push.ts +120 -0
- package/src/server/ws-router-settings.test.ts +173 -0
- package/src/server/ws-router-settings.ts +416 -0
- package/src/server/ws-router-skills.test.ts +194 -0
- package/src/server/ws-router-skills.ts +204 -0
- package/src/server/ws-router-utils.ts +238 -0
- package/src/server/ws-router.test.ts +90 -0
- package/src/server/ws-router.ts +340 -2254
- package/src/shared/app-settings-types.ts +365 -0
- package/src/shared/chat-ops.test.ts +138 -0
- package/src/shared/chat-ops.ts +71 -0
- package/src/shared/core-types.ts +27 -0
- package/src/shared/git-diff-types.ts +165 -0
- package/src/shared/mcp-types.ts +105 -0
- package/src/shared/protocol.ts +3 -2
- package/src/shared/provider-model-types.ts +437 -0
- package/src/shared/subagent-types.ts +198 -0
- package/src/shared/tool-call-types.ts +314 -0
- package/src/shared/transcript-types.ts +270 -0
- package/src/shared/types.ts +31 -1711
- package/dist/client/assets/channel-KQhRwDzJ.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-ChxT1GNq.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-ChxT1GNq.js +0 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-D4X-ZnLV.js +0 -1
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ws-router-broadcast.ts
|
|
3
|
+
*
|
|
4
|
+
* BroadcastManager — owns the connected-socket set, pending-broadcast state,
|
|
5
|
+
* and all snapshot-push / broadcast methods. Wires event subscriptions
|
|
6
|
+
* (terminals, keybindings, appSettings, update, ptyInstances, workflows, orch)
|
|
7
|
+
* and exposes a `dispose()` to tear them down.
|
|
8
|
+
*
|
|
9
|
+
* Extracted from ws-router.ts to reduce its size.
|
|
10
|
+
*/
|
|
11
|
+
import { log } from "../shared/log"
|
|
12
|
+
import { PROTOCOL_VERSION } from "../shared/types"
|
|
13
|
+
import type { ServerWebSocket } from "bun"
|
|
14
|
+
import type { PtyInstanceDelta } from "../shared/pty-instance"
|
|
15
|
+
import type { PtyInstanceRegistry } from "./claude-pty/pty-instance-registry"
|
|
16
|
+
import type { WorkflowRegistry } from "./workflow-registry"
|
|
17
|
+
import type { EventStore } from "./event-store"
|
|
18
|
+
import type { AgentCoordinator } from "./agent"
|
|
19
|
+
import type { TerminalManager } from "./terminal-manager"
|
|
20
|
+
import type { KeybindingsManager } from "./keybindings"
|
|
21
|
+
import type { UpdateManager } from "./update-manager"
|
|
22
|
+
import type { ServerEnvelope } from "../shared/protocol"
|
|
23
|
+
import type { ResolvedAppSettings } from "./ws-router-defaults"
|
|
24
|
+
import type { EnvelopeBuilder } from "./ws-router-envelope"
|
|
25
|
+
import {
|
|
26
|
+
countSubscriptionsByTopic,
|
|
27
|
+
ensureChatOpSeqMap,
|
|
28
|
+
ensureSnapshotSignatures,
|
|
29
|
+
getStableChatSnapshotSignature,
|
|
30
|
+
isSendToStartingProfilingEnabled,
|
|
31
|
+
logSendToStartingProfile,
|
|
32
|
+
send,
|
|
33
|
+
shouldIncludeTopic,
|
|
34
|
+
} from "./ws-router-utils"
|
|
35
|
+
import { diffChatMeta } from "./chat-ops-diff"
|
|
36
|
+
import type { ChatMetaSignatures } from "./chat-ops-diff"
|
|
37
|
+
import type { ClientState, SnapshotBroadcastFilter, SnapshotComputationCache } from "./ws-router-utils"
|
|
38
|
+
|
|
39
|
+
// ── Deps ──────────────────────────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
export interface BroadcastManagerDeps {
|
|
42
|
+
agent: AgentCoordinator
|
|
43
|
+
store: EventStore
|
|
44
|
+
terminals: TerminalManager
|
|
45
|
+
keybindings: KeybindingsManager
|
|
46
|
+
resolvedAppSettings: ResolvedAppSettings
|
|
47
|
+
updateManager: UpdateManager | null
|
|
48
|
+
ptyInstances?: PtyInstanceRegistry
|
|
49
|
+
workflowRegistry?: WorkflowRegistry
|
|
50
|
+
envelopeBuilder: EnvelopeBuilder
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ── BroadcastManager ─────────────────────────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
export class BroadcastManager {
|
|
56
|
+
private readonly sockets = new Set<ServerWebSocket<ClientState>>()
|
|
57
|
+
/** Last-recorded meta signatures per chat for the chat.ops diff. */
|
|
58
|
+
private readonly metaSigsByChatId = new Map<string, ChatMetaSignatures>()
|
|
59
|
+
private pendingBroadcastTimer: ReturnType<typeof setTimeout> | null = null
|
|
60
|
+
private pendingBroadcastAll = false
|
|
61
|
+
private readonly pendingBroadcastChatIds = new Set<string>()
|
|
62
|
+
|
|
63
|
+
private readonly disposeTerminalEvents: () => void
|
|
64
|
+
private readonly disposeKeybindingEvents: () => void
|
|
65
|
+
private readonly disposeAppSettingsEvents: () => void
|
|
66
|
+
private readonly disposeUpdateEvents: () => void
|
|
67
|
+
private readonly disposePtyInstances: () => void
|
|
68
|
+
private readonly disposeWorkflows: () => void
|
|
69
|
+
private readonly disposeOrchRuns: () => void
|
|
70
|
+
|
|
71
|
+
constructor(private readonly deps: BroadcastManagerDeps) {
|
|
72
|
+
const {
|
|
73
|
+
agent,
|
|
74
|
+
terminals,
|
|
75
|
+
keybindings,
|
|
76
|
+
resolvedAppSettings,
|
|
77
|
+
updateManager,
|
|
78
|
+
ptyInstances,
|
|
79
|
+
workflowRegistry,
|
|
80
|
+
store,
|
|
81
|
+
} = deps
|
|
82
|
+
|
|
83
|
+
// Wire background error reporter
|
|
84
|
+
agent.setBackgroundErrorReporter?.(this.broadcastError.bind(this))
|
|
85
|
+
|
|
86
|
+
// Terminal events
|
|
87
|
+
this.disposeTerminalEvents = terminals.onEvent((event) => {
|
|
88
|
+
this.pushTerminalEvent(event.terminalId, event)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// Keybinding snapshot push on change
|
|
92
|
+
this.disposeKeybindingEvents = keybindings.onChange(() => {
|
|
93
|
+
for (const ws of this.sockets) {
|
|
94
|
+
const snapshotSignatures = ensureSnapshotSignatures(ws)
|
|
95
|
+
for (const [id, topic] of ws.data.subscriptions.entries()) {
|
|
96
|
+
if (topic.type !== "keybindings") continue
|
|
97
|
+
const envelope = deps.envelopeBuilder.createEnvelope(id, topic, undefined, ws)
|
|
98
|
+
if (envelope.type !== "snapshot") continue
|
|
99
|
+
const signature = JSON.stringify(envelope.snapshot)
|
|
100
|
+
if (snapshotSignatures.get(id) === signature) continue
|
|
101
|
+
snapshotSignatures.set(id, signature)
|
|
102
|
+
send(ws, envelope)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
// App-settings snapshot push on change
|
|
108
|
+
this.disposeAppSettingsEvents = resolvedAppSettings.onChange(() => {
|
|
109
|
+
for (const ws of this.sockets) {
|
|
110
|
+
const snapshotSignatures = ensureSnapshotSignatures(ws)
|
|
111
|
+
for (const [id, topic] of ws.data.subscriptions.entries()) {
|
|
112
|
+
if (topic.type !== "app-settings") continue
|
|
113
|
+
const envelope = deps.envelopeBuilder.createEnvelope(id, topic, undefined, ws)
|
|
114
|
+
if (envelope.type !== "snapshot") continue
|
|
115
|
+
const signature = JSON.stringify(envelope.snapshot)
|
|
116
|
+
if (snapshotSignatures.get(id) === signature) continue
|
|
117
|
+
snapshotSignatures.set(id, signature)
|
|
118
|
+
send(ws, envelope)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
// Update snapshot push on change
|
|
124
|
+
this.disposeUpdateEvents = updateManager?.onChange(() => {
|
|
125
|
+
for (const ws of this.sockets) {
|
|
126
|
+
const snapshotSignatures = ensureSnapshotSignatures(ws)
|
|
127
|
+
for (const [id, topic] of ws.data.subscriptions.entries()) {
|
|
128
|
+
if (topic.type !== "update") continue
|
|
129
|
+
const envelope = deps.envelopeBuilder.createEnvelope(id, topic, undefined, ws)
|
|
130
|
+
if (envelope.type !== "snapshot") continue
|
|
131
|
+
const signature = JSON.stringify(envelope.snapshot)
|
|
132
|
+
if (snapshotSignatures.get(id) === signature) continue
|
|
133
|
+
snapshotSignatures.set(id, signature)
|
|
134
|
+
send(ws, envelope)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}) ?? (() => {})
|
|
138
|
+
|
|
139
|
+
// PTY instance events
|
|
140
|
+
this.disposePtyInstances = ptyInstances?.subscribe((delta: PtyInstanceDelta) => {
|
|
141
|
+
if (delta.type === "added") {
|
|
142
|
+
this.pushPtyInstancesEvent({ type: "pty-instances.added", instance: delta.instance })
|
|
143
|
+
} else if (delta.type === "updated") {
|
|
144
|
+
this.pushPtyInstancesEvent({ type: "pty-instances.updated", instance: delta.instance })
|
|
145
|
+
} else {
|
|
146
|
+
this.pushPtyInstancesEvent({ type: "pty-instances.removed", chatId: delta.chatId })
|
|
147
|
+
}
|
|
148
|
+
}) ?? (() => {})
|
|
149
|
+
|
|
150
|
+
// Workflow snapshot push on change
|
|
151
|
+
this.disposeWorkflows = workflowRegistry?.subscribe((chatId) => {
|
|
152
|
+
for (const ws of this.sockets) {
|
|
153
|
+
const snapshotSignatures = ensureSnapshotSignatures(ws)
|
|
154
|
+
for (const [id, topic] of ws.data.subscriptions.entries()) {
|
|
155
|
+
if (topic.type !== "workflows" || topic.chatId !== chatId) continue
|
|
156
|
+
const envelope = deps.envelopeBuilder.createEnvelope(id, topic, undefined, ws)
|
|
157
|
+
if (envelope.type !== "snapshot") continue
|
|
158
|
+
const signature = JSON.stringify(envelope.snapshot)
|
|
159
|
+
if (snapshotSignatures.get(id) === signature) continue
|
|
160
|
+
snapshotSignatures.set(id, signature)
|
|
161
|
+
send(ws, envelope)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}) ?? (() => {})
|
|
165
|
+
|
|
166
|
+
// Orchestration run pushes
|
|
167
|
+
// Optional like the registry subscriptions above: partial store fakes in
|
|
168
|
+
// tests may not implement it. The real EventStore always does.
|
|
169
|
+
this.disposeOrchRuns = typeof store.subscribeOrchRuns === "function"
|
|
170
|
+
? store.subscribeOrchRuns(() => this.pushOrchRuns())
|
|
171
|
+
: () => {}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ── Socket lifecycle ────────────────────────────────────────────────────────
|
|
175
|
+
|
|
176
|
+
addSocket(ws: ServerWebSocket<ClientState>): void {
|
|
177
|
+
this.sockets.add(ws)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
removeSocket(ws: ServerWebSocket<ClientState>): void {
|
|
181
|
+
this.sockets.delete(ws)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ── Protected-chat helpers ──────────────────────────────────────────────────
|
|
185
|
+
|
|
186
|
+
getProtectedChatIds(): Set<string> {
|
|
187
|
+
const { agent } = this.deps
|
|
188
|
+
const activeStatuses = agent.getActiveStatuses()
|
|
189
|
+
const drainingChatIds = typeof agent.getDrainingChatIds === "function"
|
|
190
|
+
? agent.getDrainingChatIds()
|
|
191
|
+
: new Set<string>()
|
|
192
|
+
return new Set([
|
|
193
|
+
...activeStatuses.keys(),
|
|
194
|
+
...drainingChatIds.values(),
|
|
195
|
+
])
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
getProtectedDraftChatIds(extraSockets?: Iterable<ServerWebSocket<ClientState>>): Set<string> {
|
|
199
|
+
const protectedChatIds = new Set<string>()
|
|
200
|
+
|
|
201
|
+
for (const socket of this.sockets) {
|
|
202
|
+
for (const chatId of socket.data.protectedDraftChatIds ?? []) {
|
|
203
|
+
protectedChatIds.add(chatId)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
for (const socket of extraSockets ?? []) {
|
|
208
|
+
for (const chatId of socket.data.protectedDraftChatIds ?? []) {
|
|
209
|
+
protectedChatIds.add(chatId)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return protectedChatIds
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async maybePruneStaleEmptyChats(extraSockets?: Iterable<ServerWebSocket<ClientState>>): Promise<void> {
|
|
217
|
+
const { store } = this.deps
|
|
218
|
+
const startedAt = performance.now()
|
|
219
|
+
const activeChatIds = this.getProtectedChatIds()
|
|
220
|
+
const protectedDraftChatIds = this.getProtectedDraftChatIds(extraSockets)
|
|
221
|
+
const prunedChatIds = await store.pruneStaleEmptyChats?.({
|
|
222
|
+
activeChatIds,
|
|
223
|
+
protectedChatIds: protectedDraftChatIds,
|
|
224
|
+
})
|
|
225
|
+
if (isSendToStartingProfilingEnabled()) {
|
|
226
|
+
log.info("[kanna/send->starting][server]", JSON.stringify({
|
|
227
|
+
stage: "ws.prune_stale_empty_chats",
|
|
228
|
+
elapsedMs: Number((performance.now() - startedAt).toFixed(1)),
|
|
229
|
+
activeChatCount: activeChatIds.size,
|
|
230
|
+
protectedDraftChatCount: protectedDraftChatIds.size,
|
|
231
|
+
prunedCount: prunedChatIds?.length ?? 0,
|
|
232
|
+
totalChatCount: store.state.chatsById.size,
|
|
233
|
+
totalProjectCount: store.state.projectsById.size,
|
|
234
|
+
}))
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ── Chat ops (delta) path ───────────────────────────────────────────────────
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Records runtime/section/pending delta ops for a chat into the op-log
|
|
242
|
+
* (once per broadcast pass — deduped via the shared computation cache).
|
|
243
|
+
* Ordering authority is the op-log itself: subscribers only ever read
|
|
244
|
+
* batches out of `chatOps.since`.
|
|
245
|
+
*/
|
|
246
|
+
private recordMetaOps(chatId: string, cache?: SnapshotComputationCache): void {
|
|
247
|
+
const { store, envelopeBuilder } = this.deps
|
|
248
|
+
if (typeof store.chatOps?.record !== "function") return
|
|
249
|
+
if (typeof envelopeBuilder.deriveChatMeta !== "function") return
|
|
250
|
+
if (cache) {
|
|
251
|
+
cache.chatMetaOpsRecordedChatIds ??= new Set()
|
|
252
|
+
if (cache.chatMetaOpsRecordedChatIds.has(chatId)) return
|
|
253
|
+
cache.chatMetaOpsRecordedChatIds.add(chatId)
|
|
254
|
+
}
|
|
255
|
+
const meta = envelopeBuilder.deriveChatMeta(chatId)
|
|
256
|
+
if (!meta) return
|
|
257
|
+
const { ops, next } = diffChatMeta(this.metaSigsByChatId.get(chatId), meta)
|
|
258
|
+
this.metaSigsByChatId.set(chatId, next)
|
|
259
|
+
for (const op of ops) {
|
|
260
|
+
store.chatOps.record(chatId, op)
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ── Snapshot push ───────────────────────────────────────────────────────────
|
|
265
|
+
|
|
266
|
+
async pushSnapshots(
|
|
267
|
+
ws: ServerWebSocket<ClientState>,
|
|
268
|
+
options?: { skipPrune?: boolean; filter?: SnapshotBroadcastFilter; cache?: SnapshotComputationCache }
|
|
269
|
+
): Promise<void> {
|
|
270
|
+
const { agent } = this.deps
|
|
271
|
+
const pushStartedAt = performance.now()
|
|
272
|
+
if (!options?.skipPrune) {
|
|
273
|
+
await this.maybePruneStaleEmptyChats([ws])
|
|
274
|
+
}
|
|
275
|
+
const snapshotSignatures = ensureSnapshotSignatures(ws)
|
|
276
|
+
let sentCount = 0
|
|
277
|
+
let skippedCount = 0
|
|
278
|
+
for (const [id, topic] of ws.data.subscriptions.entries()) {
|
|
279
|
+
if (!shouldIncludeTopic(topic, options?.filter)) {
|
|
280
|
+
continue
|
|
281
|
+
}
|
|
282
|
+
if (topic.type === "chat" && typeof this.deps.store.chatOps?.since === "function") {
|
|
283
|
+
const seqMap = ensureChatOpSeqMap(ws)
|
|
284
|
+
const tracked = seqMap.get(id)
|
|
285
|
+
if (tracked !== undefined) {
|
|
286
|
+
this.recordMetaOps(topic.chatId, options?.cache)
|
|
287
|
+
const batch = this.deps.store.chatOps.since(topic.chatId, tracked)
|
|
288
|
+
if (batch !== null) {
|
|
289
|
+
if (batch.ops.length === 0) {
|
|
290
|
+
skippedCount += 1
|
|
291
|
+
continue
|
|
292
|
+
}
|
|
293
|
+
seqMap.set(id, batch.toSeq)
|
|
294
|
+
send(ws, {
|
|
295
|
+
v: PROTOCOL_VERSION,
|
|
296
|
+
type: "event",
|
|
297
|
+
id,
|
|
298
|
+
event: {
|
|
299
|
+
type: "chat.ops",
|
|
300
|
+
chatId: topic.chatId,
|
|
301
|
+
fromSeq: batch.fromSeq,
|
|
302
|
+
toSeq: batch.toSeq,
|
|
303
|
+
ops: batch.ops,
|
|
304
|
+
},
|
|
305
|
+
})
|
|
306
|
+
sentCount += 1
|
|
307
|
+
continue
|
|
308
|
+
}
|
|
309
|
+
// Ring gap — drop tracking AND the snapshot signature so the
|
|
310
|
+
// fallback full snapshot below always sends and re-arms the
|
|
311
|
+
// delta path (a signature-dedup skip here would strand the
|
|
312
|
+
// subscriber outside both paths).
|
|
313
|
+
seqMap.delete(id)
|
|
314
|
+
snapshotSignatures.delete(id)
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
const envelopeStartedAt = performance.now()
|
|
318
|
+
const envelope = this.deps.envelopeBuilder.createEnvelope(id, topic, options?.cache, ws)
|
|
319
|
+
const createdAt = performance.now()
|
|
320
|
+
if (envelope.type !== "snapshot") continue
|
|
321
|
+
const signature = topic.type === "sidebar"
|
|
322
|
+
? this.deps.envelopeBuilder.getSidebarSnapshotCacheEntry(options?.cache).signature
|
|
323
|
+
: getStableChatSnapshotSignature(envelope.snapshot)
|
|
324
|
+
const signatureReadyAt = topic.type === "sidebar" ? createdAt : performance.now()
|
|
325
|
+
if (snapshotSignatures.get(id) === signature) {
|
|
326
|
+
skippedCount += 1
|
|
327
|
+
continue
|
|
328
|
+
}
|
|
329
|
+
snapshotSignatures.set(id, signature)
|
|
330
|
+
if (topic.type === "chat" && envelope.snapshot.type === "chat" && envelope.snapshot.data?.runtime.status === "starting") {
|
|
331
|
+
const profile = agent.getActiveTurnProfile(topic.chatId)
|
|
332
|
+
logSendToStartingProfile(profile?.traceId, profile?.startedAt, "ws.snapshot_sent", {
|
|
333
|
+
chatId: topic.chatId,
|
|
334
|
+
status: envelope.snapshot.data.runtime.status,
|
|
335
|
+
messageCount: envelope.snapshot.data.messages.length,
|
|
336
|
+
buildMs: Number((createdAt - envelopeStartedAt).toFixed(1)),
|
|
337
|
+
signatureMs: Number((signatureReadyAt - createdAt).toFixed(1)),
|
|
338
|
+
signatureBytes: signature.length,
|
|
339
|
+
})
|
|
340
|
+
}
|
|
341
|
+
const payloadBytes = send(ws, envelope)
|
|
342
|
+
sentCount += 1
|
|
343
|
+
if (topic.type === "chat" && envelope.snapshot.type === "chat") {
|
|
344
|
+
const dataSeq = envelope.snapshot.data?.seq
|
|
345
|
+
if (dataSeq !== undefined) {
|
|
346
|
+
ensureChatOpSeqMap(ws).set(id, dataSeq)
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (topic.type === "chat" && envelope.snapshot.type === "chat" && envelope.snapshot.data?.runtime.status === "starting") {
|
|
350
|
+
const profile = agent.getActiveTurnProfile(topic.chatId)
|
|
351
|
+
logSendToStartingProfile(profile?.traceId, profile?.startedAt, "ws.snapshot_send_completed", {
|
|
352
|
+
chatId: topic.chatId,
|
|
353
|
+
payloadBytes,
|
|
354
|
+
})
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (isSendToStartingProfilingEnabled()) {
|
|
358
|
+
log.info("[kanna/send->starting][server]", JSON.stringify({
|
|
359
|
+
stage: "ws.push_snapshots_completed",
|
|
360
|
+
elapsedMs: Number((performance.now() - pushStartedAt).toFixed(1)),
|
|
361
|
+
skipPrune: Boolean(options?.skipPrune),
|
|
362
|
+
sentCount,
|
|
363
|
+
skippedCount,
|
|
364
|
+
...countSubscriptionsByTopic(ws),
|
|
365
|
+
}))
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// ── Broadcast ───────────────────────────────────────────────────────────────
|
|
370
|
+
|
|
371
|
+
async broadcastSnapshots(): Promise<void> {
|
|
372
|
+
const { store } = this.deps
|
|
373
|
+
const startedAt = performance.now()
|
|
374
|
+
let socketCount = 0
|
|
375
|
+
const cache: SnapshotComputationCache = {}
|
|
376
|
+
for (const ws of this.sockets) {
|
|
377
|
+
socketCount += 1
|
|
378
|
+
await this.pushSnapshots(ws, { skipPrune: true, cache })
|
|
379
|
+
}
|
|
380
|
+
if (isSendToStartingProfilingEnabled()) {
|
|
381
|
+
log.info("[kanna/send->starting][server]", JSON.stringify({
|
|
382
|
+
stage: "ws.broadcast_snapshots_completed",
|
|
383
|
+
elapsedMs: Number((performance.now() - startedAt).toFixed(1)),
|
|
384
|
+
pruneMs: 0,
|
|
385
|
+
socketCount,
|
|
386
|
+
totalChatCount: store.state.chatsById.size,
|
|
387
|
+
totalProjectCount: store.state.projectsById.size,
|
|
388
|
+
}))
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async broadcastFilteredSnapshots(filter: SnapshotBroadcastFilter): Promise<void> {
|
|
393
|
+
const startedAt = performance.now()
|
|
394
|
+
let socketCount = 0
|
|
395
|
+
const cache: SnapshotComputationCache = {}
|
|
396
|
+
for (const ws of this.sockets) {
|
|
397
|
+
socketCount += 1
|
|
398
|
+
await this.pushSnapshots(ws, { skipPrune: true, filter, cache })
|
|
399
|
+
}
|
|
400
|
+
if (isSendToStartingProfilingEnabled()) {
|
|
401
|
+
log.info("[kanna/send->starting][server]", JSON.stringify({
|
|
402
|
+
stage: "ws.broadcast_filtered_snapshots_completed",
|
|
403
|
+
elapsedMs: Number((performance.now() - startedAt).toFixed(1)),
|
|
404
|
+
socketCount,
|
|
405
|
+
includeSidebar: Boolean(filter.includeSidebar),
|
|
406
|
+
chatCount: filter.chatIds?.size ?? 0,
|
|
407
|
+
projectCount: filter.projectIds?.size ?? 0,
|
|
408
|
+
}))
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
scheduleBroadcast(): void {
|
|
413
|
+
this.pendingBroadcastAll = true
|
|
414
|
+
this.pendingBroadcastChatIds.clear()
|
|
415
|
+
if (this.pendingBroadcastTimer) {
|
|
416
|
+
return
|
|
417
|
+
}
|
|
418
|
+
this.pendingBroadcastTimer = setTimeout(() => {
|
|
419
|
+
this.pendingBroadcastTimer = null
|
|
420
|
+
const shouldBroadcastAll = this.pendingBroadcastAll
|
|
421
|
+
const chatIds = new Set(this.pendingBroadcastChatIds)
|
|
422
|
+
this.pendingBroadcastAll = false
|
|
423
|
+
this.pendingBroadcastChatIds.clear()
|
|
424
|
+
if (shouldBroadcastAll) {
|
|
425
|
+
void this.broadcastSnapshots()
|
|
426
|
+
return
|
|
427
|
+
}
|
|
428
|
+
if (chatIds.size > 0) {
|
|
429
|
+
void this.broadcastFilteredSnapshots({
|
|
430
|
+
includeSidebar: true,
|
|
431
|
+
chatIds,
|
|
432
|
+
})
|
|
433
|
+
}
|
|
434
|
+
}, 16)
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
scheduleChatStateBroadcast(chatId: string): void {
|
|
438
|
+
if (!this.pendingBroadcastAll) {
|
|
439
|
+
this.pendingBroadcastChatIds.add(chatId)
|
|
440
|
+
}
|
|
441
|
+
if (this.pendingBroadcastTimer) {
|
|
442
|
+
return
|
|
443
|
+
}
|
|
444
|
+
this.pendingBroadcastTimer = setTimeout(() => {
|
|
445
|
+
this.pendingBroadcastTimer = null
|
|
446
|
+
const shouldBroadcastAll = this.pendingBroadcastAll
|
|
447
|
+
const chatIds = new Set(this.pendingBroadcastChatIds)
|
|
448
|
+
this.pendingBroadcastAll = false
|
|
449
|
+
this.pendingBroadcastChatIds.clear()
|
|
450
|
+
if (shouldBroadcastAll) {
|
|
451
|
+
void this.broadcastSnapshots()
|
|
452
|
+
return
|
|
453
|
+
}
|
|
454
|
+
if (chatIds.size > 0) {
|
|
455
|
+
void this.broadcastFilteredSnapshots({
|
|
456
|
+
includeSidebar: true,
|
|
457
|
+
chatIds,
|
|
458
|
+
})
|
|
459
|
+
}
|
|
460
|
+
}, 16)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
async broadcastChatAndSidebar(chatId: string): Promise<void> {
|
|
464
|
+
await this.broadcastFilteredSnapshots({
|
|
465
|
+
includeSidebar: true,
|
|
466
|
+
chatIds: new Set([chatId]),
|
|
467
|
+
})
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
async broadcastChatStateImmediately(chatId: string): Promise<void> {
|
|
471
|
+
await this.broadcastChatAndSidebar(chatId)
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
broadcastError(message: string): void {
|
|
475
|
+
for (const ws of this.sockets) {
|
|
476
|
+
send(ws, {
|
|
477
|
+
v: PROTOCOL_VERSION,
|
|
478
|
+
type: "error",
|
|
479
|
+
message,
|
|
480
|
+
})
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// ── Per-topic targeted pushes ───────────────────────────────────────────────
|
|
485
|
+
|
|
486
|
+
pushTerminalSnapshot(terminalId: string): void {
|
|
487
|
+
for (const ws of this.sockets) {
|
|
488
|
+
const snapshotSignatures = ensureSnapshotSignatures(ws)
|
|
489
|
+
for (const [id, topic] of ws.data.subscriptions.entries()) {
|
|
490
|
+
if (topic.type !== "terminal" || topic.terminalId !== terminalId) continue
|
|
491
|
+
const envelope = this.deps.envelopeBuilder.createEnvelope(id, topic, undefined, ws)
|
|
492
|
+
if (envelope.type !== "snapshot") continue
|
|
493
|
+
const signature = JSON.stringify(envelope.snapshot)
|
|
494
|
+
if (snapshotSignatures.get(id) === signature) continue
|
|
495
|
+
snapshotSignatures.set(id, signature)
|
|
496
|
+
send(ws, envelope)
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
pushTerminalEvent(
|
|
502
|
+
terminalId: string,
|
|
503
|
+
event: Extract<ServerEnvelope, { type: "event" }>["event"]
|
|
504
|
+
): void {
|
|
505
|
+
for (const ws of this.sockets) {
|
|
506
|
+
for (const [id, topic] of ws.data.subscriptions.entries()) {
|
|
507
|
+
if (topic.type !== "terminal" || topic.terminalId !== terminalId) continue
|
|
508
|
+
send(ws, {
|
|
509
|
+
v: PROTOCOL_VERSION,
|
|
510
|
+
type: "event",
|
|
511
|
+
id,
|
|
512
|
+
event,
|
|
513
|
+
})
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
private pushPtyInstancesEvent(
|
|
519
|
+
event: Extract<ServerEnvelope, { type: "event" }>["event"]
|
|
520
|
+
): void {
|
|
521
|
+
for (const ws of this.sockets) {
|
|
522
|
+
for (const [id, topic] of ws.data.subscriptions.entries()) {
|
|
523
|
+
if (topic.type !== "pty-instances") continue
|
|
524
|
+
send(ws, { v: PROTOCOL_VERSION, type: "event", id, event })
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
private pushOrchRuns(): void {
|
|
530
|
+
for (const ws of this.sockets) {
|
|
531
|
+
const snapshotSignatures = ensureSnapshotSignatures(ws)
|
|
532
|
+
for (const [id, topic] of ws.data.subscriptions.entries()) {
|
|
533
|
+
if (topic.type !== "orch-runs") continue
|
|
534
|
+
const envelope = this.deps.envelopeBuilder.createEnvelope(id, topic, undefined, ws)
|
|
535
|
+
if (envelope.type !== "snapshot") continue
|
|
536
|
+
const signature = JSON.stringify(envelope.snapshot)
|
|
537
|
+
if (snapshotSignatures.get(id) === signature) continue
|
|
538
|
+
snapshotSignatures.set(id, signature)
|
|
539
|
+
send(ws, envelope)
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// ── Cleanup ─────────────────────────────────────────────────────────────────
|
|
545
|
+
|
|
546
|
+
dispose(): void {
|
|
547
|
+
if (this.pendingBroadcastTimer) {
|
|
548
|
+
clearTimeout(this.pendingBroadcastTimer)
|
|
549
|
+
this.pendingBroadcastTimer = null
|
|
550
|
+
}
|
|
551
|
+
this.deps.agent.setBackgroundErrorReporter?.(null)
|
|
552
|
+
this.disposeTerminalEvents()
|
|
553
|
+
this.disposeKeybindingEvents()
|
|
554
|
+
this.disposeAppSettingsEvents()
|
|
555
|
+
this.disposeUpdateEvents()
|
|
556
|
+
this.disposePtyInstances()
|
|
557
|
+
this.disposeWorkflows()
|
|
558
|
+
this.disposeOrchRuns()
|
|
559
|
+
}
|
|
560
|
+
}
|