@cuongtran001/kanna 1.2.0 → 1.3.1
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-BLk1CZKB.js → arc-DXtN3EWJ.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-B01ZQYte.js → architectureDiagram-3BPJPVTR-CIGqW1z-.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-diL6a3cq.js → blockDiagram-GPEHLZMM-BafNS_H6.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-BY1verL8.js → c4Diagram-AAUBKEIU-YdAgRSis.js} +1 -1
- package/dist/client/assets/channel-BEYTrUAm.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-psdZuajw.js → chunk-2J33WTMH-eiP5y5G-.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-CrwQVBc5.js → chunk-4BX2VUAB-CvLGzPMF.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-9-aj4jKO.js → chunk-55IACEB6-Y-S9Fb8A.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-1FNK9_nB.js → chunk-727SXJPM-6sJH1RI9.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-DFhvd_Ek.js → chunk-AQP2D5EJ-hV1r_IXO.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-B7MLrDa7.js → chunk-FMBD7UC4-CC3L28S5.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-D4Df05QE.js → chunk-ND2GUHAM-BZ3czhdH.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-Iq17Dznm.js → chunk-QZHKN3VN-Bjyd6Yr-.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-CvnF5Ud6.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CvnF5Ud6.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-BmaZhtXy.js → cose-bilkent-S5V4N54A-CfxdDghE.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-BCmpfnfP.js → dagre-BM42HDAG-B8CddsbB.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-BIEcKowS.js → diagram-2AECGRRQ-F0yS0fVB.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-CRBABBfv.js → diagram-5GNKFQAL-cW0i0qlh.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-BRxUk16N.js → diagram-KO2AKTUF-BzAKMeJT.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-DPBs4xl1.js → diagram-LMA3HP47-qdC4XqIl.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-BE9rgVv8.js → diagram-OG6HWLK6-CGcpi6Dh.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-D3vgA1xJ.js → erDiagram-TEJ5UH35-CxNTDicM.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-Cgu-8XXC.js → flowDiagram-I6XJVG4X-qkGa5TqY.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-CgjXVyTR.js → ganttDiagram-6RSMTGT7-D9mVPoQi.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-zD5D8B-i.js → gitGraphDiagram-PVQCEYII-DA0grAXe.js} +1 -1
- package/dist/client/assets/{index-DBTdhMbJ.js → index-BqUY1Bm4.js} +1 -1
- package/dist/client/assets/index-CLsOWXff.css +32 -0
- package/dist/client/assets/{index-zQLLeAfl.js → index-TU1k6ov6.js} +253 -243
- package/dist/client/assets/{infoDiagram-5YYISTIA-BaFHqT4u.js → infoDiagram-5YYISTIA-CGljtLs3.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-2yAC1br2.js → ishikawaDiagram-YF4QCWOH-CtC0xrmG.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-DacQ-ND5.js → journeyDiagram-JHISSGLW-URLnVZeN.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-Qlbwm3z4.js → kanban-definition-UN3LZRKU-C2Q6aqQh.js} +1 -1
- package/dist/client/assets/{linear-DeafqpgO.js → linear-ByKHtsIx.js} +1 -1
- package/dist/client/assets/{mermaid.core-KYiCPJDj.js → mermaid.core-Dl_f2KLc.js} +4 -4
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-B1V-T6d3.js → mindmap-definition-RKZ34NQL-CaJ9IDLI.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-DOF9OFRu.js → pieDiagram-4H26LBE5-mmYaf2Oa.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-lH1mB7ko.js → quadrantDiagram-W4KKPZXB-B_PO6OCC.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-BaTsJ_sS.js → requirementDiagram-4Y6WPE33-M4huwJt1.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-Pfxib5iI.js → sankeyDiagram-5OEKKPKP-B0hb1sWh.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BQMEKkBC.js → sequenceDiagram-3UESZ5HK-BAqSptnA.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-CuldNnzp.js → stateDiagram-AJRCARHV-DTNkvPi5.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CVN3oGTF.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-DP7u0YAg.js → timeline-definition-PNZ67QCA-MgAjHz8Q.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-QlrjpNeQ.js → vennDiagram-CIIHVFJN-DmKoO51W.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-BqdJ_sAM.js → wardley-L42UT6IY-CWcyHnYs.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-Wi3kaQ5X.js → wardleyDiagram-YWT4CUSO-BGIGmFP9.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-D-KXMyCG.js → xychartDiagram-2RQKCTM6-D20YIvEl.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.oauth-rotation.test.ts +101 -0
- package/src/server/agent.test.ts +24 -8
- package/src/server/agent.ts +35 -4963
- package/src/server/auto-continue/read-model.test.ts +2 -2
- package/src/server/auto-continue/read-model.ts +4 -1
- 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 +365 -0
- package/src/server/event-store-messages.adapter.ts +213 -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 +285 -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 +35 -0
- package/src/server/event-store.ts +251 -2284
- package/src/server/events.ts +3 -0
- package/src/server/harness-types.ts +15 -1
- package/src/server/oauth-errors.ts +19 -0
- package/src/server/read-models.ts +26 -1
- package/src/server/subagent-orchestrator.ts +3 -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 +488 -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 +329 -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 +224 -0
- package/src/server/ws-router.ts +339 -2254
- package/src/shared/app-settings-types.ts +365 -0
- package/src/shared/core-types.ts +27 -0
- package/src/shared/git-diff-types.ts +165 -0
- package/src/shared/loop-progress.test.ts +106 -0
- package/src/shared/loop-progress.ts +88 -0
- package/src/shared/mcp-types.ts +105 -0
- 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 +27 -1668
- package/dist/client/assets/channel-BMkhjiLI.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-C5Kg1XOD.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-C5Kg1XOD.js +0 -1
- package/dist/client/assets/index-H6C-f2Vp.css +0 -32
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Ca2U11af.js +0 -1
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project, stack, chat entity write operations and session/turn write ops
|
|
3
|
+
* extracted from event-store.ts.
|
|
4
|
+
*
|
|
5
|
+
* All functions follow the pattern: build an event using a builder from
|
|
6
|
+
* event-store-write-ops, then delegate to `deps.append`. This file is pure
|
|
7
|
+
* (no direct disk IO), so it does NOT carry the .adapter.ts suffix.
|
|
8
|
+
*
|
|
9
|
+
* Must NOT import from event-store.ts (no circular deps).
|
|
10
|
+
*/
|
|
11
|
+
import type { AgentProvider, QueuedChatMessage, SlashCommand, StackBinding } from "../shared/types"
|
|
12
|
+
import type { ChatPermissionPolicyOverride } from "../shared/permission-policy"
|
|
13
|
+
import type { AutoContinueEvent } from "./auto-continue/events"
|
|
14
|
+
import type { ChatRecord, StackRecord, StoreEvent, StoreState, TurnRunConfig } from "./events"
|
|
15
|
+
import type { StorageBackend } from "./storage/backend"
|
|
16
|
+
import {
|
|
17
|
+
buildAddProjectToStackEvent,
|
|
18
|
+
buildChatPolicyOverrideEvent,
|
|
19
|
+
buildChatProviderEvent,
|
|
20
|
+
buildChatReadStateEvent,
|
|
21
|
+
buildChatSourceHashEvent,
|
|
22
|
+
buildCompactFailuresEvent,
|
|
23
|
+
buildCreateChatEvent,
|
|
24
|
+
buildCreateStackEvent,
|
|
25
|
+
buildEnqueueMessageResult,
|
|
26
|
+
buildOpenProjectResult,
|
|
27
|
+
buildPendingForkSessionTokenEvent,
|
|
28
|
+
buildPlanModeEvent,
|
|
29
|
+
buildRemoveProjectEvent,
|
|
30
|
+
buildRemoveProjectFromStackEvent,
|
|
31
|
+
buildRemoveQueuedMessageEvent,
|
|
32
|
+
buildRemoveStackEvent,
|
|
33
|
+
buildRenameStackEvent,
|
|
34
|
+
buildRenameChatEvent,
|
|
35
|
+
buildSessionCommandsEvent,
|
|
36
|
+
buildSessionTokenEvent,
|
|
37
|
+
buildSetProjectStarEvent,
|
|
38
|
+
buildTurnCancelledEvent,
|
|
39
|
+
buildTurnFailedEvent,
|
|
40
|
+
buildTurnFinishedEvent,
|
|
41
|
+
buildTurnStartedEvent,
|
|
42
|
+
computeNewSidebarOrder,
|
|
43
|
+
} from "./event-store-write-ops"
|
|
44
|
+
import { writeSidebarOrderFile } from "./event-store-snapshot"
|
|
45
|
+
|
|
46
|
+
// ─── Entity write deps ────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
export interface EntityWriteDeps {
|
|
49
|
+
readonly storage: StorageBackend
|
|
50
|
+
readonly dataDir: string
|
|
51
|
+
readonly sidebarProjectOrderPath: string
|
|
52
|
+
readonly projectsLogPath: string
|
|
53
|
+
readonly chatsLogPath: string
|
|
54
|
+
readonly queuedMessagesLogPath: string
|
|
55
|
+
readonly stacksLogPath: string
|
|
56
|
+
readonly projectsById: StoreState["projectsById"]
|
|
57
|
+
readonly projectIdsByPath: StoreState["projectIdsByPath"]
|
|
58
|
+
readonly chatsById: Map<string, ChatRecord>
|
|
59
|
+
readonly queuedMessagesByChatId: StoreState["queuedMessagesByChatId"]
|
|
60
|
+
readonly stacksById: StoreState["stacksById"]
|
|
61
|
+
/** Mutable ref for the sidebar project order. */
|
|
62
|
+
readonly sidebarProjectOrderRef: { value: string[] }
|
|
63
|
+
/** Read the current write-chain promise. */
|
|
64
|
+
getWriteChain: () => Promise<void>
|
|
65
|
+
/** Replace the write-chain promise. */
|
|
66
|
+
setWriteChain: (p: Promise<void>) => void
|
|
67
|
+
/** Core append: writes event to disk and applies to in-memory state. */
|
|
68
|
+
append: <T extends StoreEvent>(filePath: string, event: T) => Promise<void>
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ─── Session / turn write deps ────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
export interface SessionWriteDeps {
|
|
74
|
+
readonly chatsById: Map<string, ChatRecord>
|
|
75
|
+
readonly turnsLogPath: string
|
|
76
|
+
readonly schedulesLogPath: string
|
|
77
|
+
append: <T extends StoreEvent>(filePath: string, event: T) => Promise<void>
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ─── Project ops ───────────────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
export async function openProject(
|
|
83
|
+
deps: EntityWriteDeps,
|
|
84
|
+
localPath: string,
|
|
85
|
+
title?: string,
|
|
86
|
+
) {
|
|
87
|
+
const result = buildOpenProjectResult(
|
|
88
|
+
{ projectsById: deps.projectsById, projectIdsByPath: deps.projectIdsByPath },
|
|
89
|
+
localPath,
|
|
90
|
+
title,
|
|
91
|
+
)
|
|
92
|
+
if (result.kind === "existing") return result.project
|
|
93
|
+
await deps.append(deps.projectsLogPath, result.event)
|
|
94
|
+
return deps.projectsById.get(result.event.projectId)!
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function removeProject(deps: EntityWriteDeps, projectId: string) {
|
|
98
|
+
const event = buildRemoveProjectEvent(deps.projectsById, projectId)
|
|
99
|
+
await deps.append(deps.projectsLogPath, event)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function setProjectStar(
|
|
103
|
+
deps: EntityWriteDeps,
|
|
104
|
+
projectId: string,
|
|
105
|
+
starred: boolean,
|
|
106
|
+
) {
|
|
107
|
+
const event = buildSetProjectStarEvent(deps.projectsById, projectId, starred)
|
|
108
|
+
await deps.append(deps.projectsLogPath, event)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ─── Stack ops ─────────────────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
export async function createStack(
|
|
114
|
+
deps: EntityWriteDeps,
|
|
115
|
+
title: string,
|
|
116
|
+
projectIds: string[],
|
|
117
|
+
): Promise<StackRecord> {
|
|
118
|
+
const event = buildCreateStackEvent(
|
|
119
|
+
{ projectsById: deps.projectsById, stacksById: deps.stacksById },
|
|
120
|
+
title,
|
|
121
|
+
projectIds,
|
|
122
|
+
)
|
|
123
|
+
await deps.append(deps.stacksLogPath, event)
|
|
124
|
+
return deps.stacksById.get(event.stackId)!
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export async function renameStack(
|
|
128
|
+
deps: EntityWriteDeps,
|
|
129
|
+
stackId: string,
|
|
130
|
+
title: string,
|
|
131
|
+
) {
|
|
132
|
+
const event = buildRenameStackEvent(deps.stacksById, stackId, title)
|
|
133
|
+
if (event) await deps.append(deps.stacksLogPath, event)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function removeStack(deps: EntityWriteDeps, stackId: string) {
|
|
137
|
+
const event = buildRemoveStackEvent(deps.stacksById, stackId)
|
|
138
|
+
if (event) await deps.append(deps.stacksLogPath, event)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export async function addProjectToStack(
|
|
142
|
+
deps: EntityWriteDeps,
|
|
143
|
+
stackId: string,
|
|
144
|
+
projectId: string,
|
|
145
|
+
) {
|
|
146
|
+
const event = buildAddProjectToStackEvent(
|
|
147
|
+
{ projectsById: deps.projectsById, stacksById: deps.stacksById },
|
|
148
|
+
stackId,
|
|
149
|
+
projectId,
|
|
150
|
+
)
|
|
151
|
+
if (event) await deps.append(deps.stacksLogPath, event)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export async function removeProjectFromStack(
|
|
155
|
+
deps: EntityWriteDeps,
|
|
156
|
+
stackId: string,
|
|
157
|
+
projectId: string,
|
|
158
|
+
) {
|
|
159
|
+
const event = buildRemoveProjectFromStackEvent(deps.stacksById, stackId, projectId)
|
|
160
|
+
if (event) await deps.append(deps.stacksLogPath, event)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ─── Sidebar ───────────────────────────────────────────────────────────────
|
|
164
|
+
|
|
165
|
+
export async function setSidebarProjectOrder(
|
|
166
|
+
deps: EntityWriteDeps,
|
|
167
|
+
projectIds: string[],
|
|
168
|
+
): Promise<void> {
|
|
169
|
+
const newOrder = computeNewSidebarOrder(
|
|
170
|
+
deps.projectsById,
|
|
171
|
+
deps.sidebarProjectOrderRef.value,
|
|
172
|
+
projectIds,
|
|
173
|
+
)
|
|
174
|
+
if (!newOrder) return
|
|
175
|
+
const newChain = deps.getWriteChain().then(async () => {
|
|
176
|
+
await writeSidebarOrderFile(deps.storage, deps.dataDir, deps.sidebarProjectOrderPath, newOrder)
|
|
177
|
+
deps.sidebarProjectOrderRef.value = [...newOrder]
|
|
178
|
+
})
|
|
179
|
+
deps.setWriteChain(newChain)
|
|
180
|
+
await newChain
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ─── Chat lifecycle ops ────────────────────────────────────────────────────
|
|
184
|
+
|
|
185
|
+
export async function createChat(
|
|
186
|
+
deps: EntityWriteDeps,
|
|
187
|
+
projectId: string,
|
|
188
|
+
options?: { stackId?: string; stackBindings?: StackBinding[] },
|
|
189
|
+
): Promise<ChatRecord> {
|
|
190
|
+
const event = buildCreateChatEvent(
|
|
191
|
+
{ projectsById: deps.projectsById, stacksById: deps.stacksById },
|
|
192
|
+
projectId,
|
|
193
|
+
options,
|
|
194
|
+
)
|
|
195
|
+
await deps.append(deps.chatsLogPath, event)
|
|
196
|
+
return deps.chatsById.get(event.chatId)!
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export async function renameChat(
|
|
200
|
+
deps: EntityWriteDeps,
|
|
201
|
+
chatId: string,
|
|
202
|
+
title: string,
|
|
203
|
+
) {
|
|
204
|
+
const event = buildRenameChatEvent(deps.chatsById, chatId, title)
|
|
205
|
+
if (event) await deps.append(deps.chatsLogPath, event)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export async function setChatProvider(
|
|
209
|
+
deps: EntityWriteDeps,
|
|
210
|
+
chatId: string,
|
|
211
|
+
provider: AgentProvider,
|
|
212
|
+
) {
|
|
213
|
+
const ev = buildChatProviderEvent(deps.chatsById, chatId, provider)
|
|
214
|
+
if (ev) await deps.append(deps.chatsLogPath, ev)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export async function setPlanMode(
|
|
218
|
+
deps: EntityWriteDeps,
|
|
219
|
+
chatId: string,
|
|
220
|
+
planMode: boolean,
|
|
221
|
+
) {
|
|
222
|
+
const ev = buildPlanModeEvent(deps.chatsById, chatId, planMode)
|
|
223
|
+
if (ev) await deps.append(deps.chatsLogPath, ev)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export async function setCompactFailureCount(
|
|
227
|
+
deps: EntityWriteDeps,
|
|
228
|
+
chatId: string,
|
|
229
|
+
compactFailureCount: number,
|
|
230
|
+
) {
|
|
231
|
+
const ev = buildCompactFailuresEvent(deps.chatsById, chatId, compactFailureCount)
|
|
232
|
+
if (ev) await deps.append(deps.chatsLogPath, ev)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export async function setChatReadState(
|
|
236
|
+
deps: EntityWriteDeps,
|
|
237
|
+
chatId: string,
|
|
238
|
+
unread: boolean,
|
|
239
|
+
) {
|
|
240
|
+
const ev = buildChatReadStateEvent(deps.chatsById, chatId, unread)
|
|
241
|
+
if (ev) await deps.append(deps.chatsLogPath, ev)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export async function setChatPolicyOverride(
|
|
245
|
+
deps: EntityWriteDeps,
|
|
246
|
+
chatId: string,
|
|
247
|
+
policyOverride: ChatPermissionPolicyOverride | null,
|
|
248
|
+
) {
|
|
249
|
+
await deps.append(
|
|
250
|
+
deps.chatsLogPath,
|
|
251
|
+
buildChatPolicyOverrideEvent(deps.chatsById, chatId, policyOverride),
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export async function setSourceHash(
|
|
256
|
+
deps: EntityWriteDeps,
|
|
257
|
+
chatId: string,
|
|
258
|
+
sourceHash: string | null,
|
|
259
|
+
) {
|
|
260
|
+
const ev = buildChatSourceHashEvent(deps.chatsById, chatId, sourceHash)
|
|
261
|
+
if (ev) await deps.append(deps.chatsLogPath, ev)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ─── Queued message ops ────────────────────────────────────────────────────
|
|
265
|
+
|
|
266
|
+
export async function enqueueMessage(
|
|
267
|
+
deps: EntityWriteDeps,
|
|
268
|
+
chatId: string,
|
|
269
|
+
message: Omit<QueuedChatMessage, "id" | "createdAt"> & Partial<Pick<QueuedChatMessage, "id" | "createdAt">>,
|
|
270
|
+
) {
|
|
271
|
+
const { event, queuedMessage } = buildEnqueueMessageResult(deps.chatsById, chatId, message)
|
|
272
|
+
await deps.append(deps.queuedMessagesLogPath, event)
|
|
273
|
+
return queuedMessage
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export async function removeQueuedMessage(
|
|
277
|
+
deps: EntityWriteDeps,
|
|
278
|
+
chatId: string,
|
|
279
|
+
queuedMessageId: string,
|
|
280
|
+
) {
|
|
281
|
+
const event = buildRemoveQueuedMessageEvent(
|
|
282
|
+
deps.chatsById, deps.queuedMessagesByChatId, chatId, queuedMessageId,
|
|
283
|
+
)
|
|
284
|
+
await deps.append(deps.queuedMessagesLogPath, event)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// ─── Session / turn write ops ─────────────────────────────────────────────
|
|
288
|
+
|
|
289
|
+
export async function recordTurnStarted(
|
|
290
|
+
deps: SessionWriteDeps,
|
|
291
|
+
chatId: string,
|
|
292
|
+
runConfig?: TurnRunConfig,
|
|
293
|
+
) {
|
|
294
|
+
await deps.append(deps.turnsLogPath, buildTurnStartedEvent(deps.chatsById, chatId, runConfig))
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export async function recordTurnFinished(deps: SessionWriteDeps, chatId: string) {
|
|
298
|
+
await deps.append(deps.turnsLogPath, buildTurnFinishedEvent(deps.chatsById, chatId))
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export async function recordTurnFailed(
|
|
302
|
+
deps: SessionWriteDeps,
|
|
303
|
+
chatId: string,
|
|
304
|
+
error: string,
|
|
305
|
+
) {
|
|
306
|
+
await deps.append(deps.turnsLogPath, buildTurnFailedEvent(deps.chatsById, chatId, error))
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export async function recordTurnCancelled(deps: SessionWriteDeps, chatId: string) {
|
|
310
|
+
await deps.append(deps.turnsLogPath, buildTurnCancelledEvent(deps.chatsById, chatId))
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export async function setSessionTokenForProvider(
|
|
314
|
+
deps: SessionWriteDeps,
|
|
315
|
+
chatId: string,
|
|
316
|
+
provider: AgentProvider,
|
|
317
|
+
sessionToken: string | null,
|
|
318
|
+
) {
|
|
319
|
+
const ev = buildSessionTokenEvent(deps.chatsById, chatId, provider, sessionToken)
|
|
320
|
+
if (ev) await deps.append(deps.turnsLogPath, ev)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export async function recordSessionCommandsLoaded(
|
|
324
|
+
deps: SessionWriteDeps,
|
|
325
|
+
chatId: string,
|
|
326
|
+
commands: SlashCommand[],
|
|
327
|
+
) {
|
|
328
|
+
const ev = buildSessionCommandsEvent(deps.chatsById, chatId, commands)
|
|
329
|
+
if (ev) await deps.append(deps.turnsLogPath, ev)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export async function setPendingForkSessionToken(
|
|
333
|
+
deps: SessionWriteDeps,
|
|
334
|
+
chatId: string,
|
|
335
|
+
value: { provider: AgentProvider; token: string } | null,
|
|
336
|
+
) {
|
|
337
|
+
const ev = buildPendingForkSessionToken(deps.chatsById, chatId, value)
|
|
338
|
+
if (ev) await deps.append(deps.turnsLogPath, ev)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export async function appendAutoContinueEvent(
|
|
342
|
+
deps: SessionWriteDeps,
|
|
343
|
+
event: AutoContinueEvent,
|
|
344
|
+
) {
|
|
345
|
+
return deps.append(deps.schedulesLogPath, event)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// ─── Private helper ───────────────────────────────────────────────────────
|
|
349
|
+
|
|
350
|
+
function buildPendingForkSessionToken(
|
|
351
|
+
chatsById: Map<string, ChatRecord>,
|
|
352
|
+
chatId: string,
|
|
353
|
+
value: { provider: AgentProvider; token: string } | null,
|
|
354
|
+
) {
|
|
355
|
+
return buildPendingForkSessionTokenEvent(chatsById, chatId, value)
|
|
356
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import type { TranscriptEntry } from "../shared/types"
|
|
3
|
+
import type { StoreEvent } from "./events"
|
|
4
|
+
import {
|
|
5
|
+
coalesceContextWindowUpdates,
|
|
6
|
+
decodeCursor,
|
|
7
|
+
encodeHistoryCursor,
|
|
8
|
+
getForkedChatTitle,
|
|
9
|
+
getHistorySnapshot,
|
|
10
|
+
getReplayEventPriority,
|
|
11
|
+
normalizeSidebarProjectOrder,
|
|
12
|
+
slashCommandsEqual,
|
|
13
|
+
type TranscriptPageResult,
|
|
14
|
+
} from "./event-store-helpers"
|
|
15
|
+
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// normalizeSidebarProjectOrder
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
describe("normalizeSidebarProjectOrder", () => {
|
|
20
|
+
test("returns empty array for non-array input", () => {
|
|
21
|
+
expect(normalizeSidebarProjectOrder(null)).toEqual([])
|
|
22
|
+
expect(normalizeSidebarProjectOrder(42)).toEqual([])
|
|
23
|
+
expect(normalizeSidebarProjectOrder({ a: 1 })).toEqual([])
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test("filters out non-string entries", () => {
|
|
27
|
+
expect(normalizeSidebarProjectOrder(["a", 2, null, "b"])).toEqual(["a", "b"])
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test("trims whitespace and skips blank strings", () => {
|
|
31
|
+
expect(normalizeSidebarProjectOrder([" a ", " ", "b"])).toEqual(["a", "b"])
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test("deduplicates preserving first occurrence", () => {
|
|
35
|
+
expect(normalizeSidebarProjectOrder(["x", "y", "x", "z"])).toEqual(["x", "y", "z"])
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test("returns ordered array for clean input", () => {
|
|
39
|
+
expect(normalizeSidebarProjectOrder(["proj-1", "proj-2"])).toEqual(["proj-1", "proj-2"])
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// encodeHistoryCursor / decodeCursor
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
describe("encodeHistoryCursor", () => {
|
|
47
|
+
test("encodes index as idx: prefix string", () => {
|
|
48
|
+
expect(encodeHistoryCursor(0)).toBe("idx:0")
|
|
49
|
+
expect(encodeHistoryCursor(42)).toBe("idx:42")
|
|
50
|
+
expect(encodeHistoryCursor(9999)).toBe("idx:9999")
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
describe("decodeCursor", () => {
|
|
55
|
+
test("round-trips with encodeHistoryCursor", () => {
|
|
56
|
+
expect(decodeCursor(encodeHistoryCursor(0))).toBe(0)
|
|
57
|
+
expect(decodeCursor(encodeHistoryCursor(100))).toBe(100)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test("throws on invalid cursor format", () => {
|
|
61
|
+
expect(() => decodeCursor("cursor:0")).toThrow("Invalid history cursor")
|
|
62
|
+
expect(() => decodeCursor("")).toThrow("Invalid history cursor")
|
|
63
|
+
expect(() => decodeCursor("idx:-1")).toThrow("Invalid history cursor")
|
|
64
|
+
expect(() => decodeCursor("idx:abc")).toThrow("Invalid history cursor")
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// slashCommandsEqual
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
describe("slashCommandsEqual", () => {
|
|
72
|
+
test("returns true for two empty arrays", () => {
|
|
73
|
+
expect(slashCommandsEqual([], [])).toBe(true)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test("returns false when lengths differ", () => {
|
|
77
|
+
expect(slashCommandsEqual(
|
|
78
|
+
[{ name: "a", description: "d", argumentHint: "" }],
|
|
79
|
+
[],
|
|
80
|
+
)).toBe(false)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test("returns true for identical arrays", () => {
|
|
84
|
+
const cmds = [{ name: "foo", description: "bar", argumentHint: "<hint>" }]
|
|
85
|
+
expect(slashCommandsEqual(cmds, cmds)).toBe(true)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test("returns false when a field differs", () => {
|
|
89
|
+
const a = [{ name: "foo", description: "bar", argumentHint: "" }]
|
|
90
|
+
const b = [{ name: "foo", description: "baz", argumentHint: "" }]
|
|
91
|
+
expect(slashCommandsEqual(a, b)).toBe(false)
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// coalesceContextWindowUpdates
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
function makeEntry(kind: TranscriptEntry["kind"], id: string): TranscriptEntry {
|
|
100
|
+
return { _id: id, createdAt: 0, kind } as TranscriptEntry
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
describe("coalesceContextWindowUpdates", () => {
|
|
104
|
+
test("returns empty array unchanged", () => {
|
|
105
|
+
expect(coalesceContextWindowUpdates([])).toEqual([])
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
test("passes through entries without context_window_updated", () => {
|
|
109
|
+
const entries = [
|
|
110
|
+
makeEntry("user_prompt", "1"),
|
|
111
|
+
makeEntry("assistant_text", "2"),
|
|
112
|
+
]
|
|
113
|
+
expect(coalesceContextWindowUpdates(entries)).toEqual(entries)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
test("collapses consecutive context_window_updated to last", () => {
|
|
117
|
+
const entries = [
|
|
118
|
+
makeEntry("context_window_updated", "cwu-1"),
|
|
119
|
+
makeEntry("context_window_updated", "cwu-2"),
|
|
120
|
+
makeEntry("context_window_updated", "cwu-3"),
|
|
121
|
+
]
|
|
122
|
+
const result = coalesceContextWindowUpdates(entries)
|
|
123
|
+
expect(result).toHaveLength(1)
|
|
124
|
+
expect(result[0]!._id).toBe("cwu-3")
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
test("preserves non-cwu entries between cwu runs", () => {
|
|
128
|
+
const entries = [
|
|
129
|
+
makeEntry("context_window_updated", "cwu-1"),
|
|
130
|
+
makeEntry("context_window_updated", "cwu-2"),
|
|
131
|
+
makeEntry("user_prompt", "up-1"),
|
|
132
|
+
makeEntry("context_window_updated", "cwu-3"),
|
|
133
|
+
makeEntry("context_window_updated", "cwu-4"),
|
|
134
|
+
]
|
|
135
|
+
const result = coalesceContextWindowUpdates(entries)
|
|
136
|
+
expect(result.map((e) => e._id)).toEqual(["cwu-2", "up-1", "cwu-4"])
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test("does not drop a single context_window_updated", () => {
|
|
140
|
+
const entries = [makeEntry("context_window_updated", "solo")]
|
|
141
|
+
expect(coalesceContextWindowUpdates(entries)).toEqual(entries)
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
// getHistorySnapshot
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
describe("getHistorySnapshot", () => {
|
|
149
|
+
test("maps page fields and recentLimit into ChatHistorySnapshot", () => {
|
|
150
|
+
const page: TranscriptPageResult = {
|
|
151
|
+
entries: [],
|
|
152
|
+
hasOlder: true,
|
|
153
|
+
olderCursor: "idx:5",
|
|
154
|
+
}
|
|
155
|
+
expect(getHistorySnapshot(page, 50)).toEqual({
|
|
156
|
+
hasOlder: true,
|
|
157
|
+
olderCursor: "idx:5",
|
|
158
|
+
recentLimit: 50,
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
test("passes null olderCursor through", () => {
|
|
163
|
+
const page: TranscriptPageResult = { entries: [], hasOlder: false, olderCursor: null }
|
|
164
|
+
expect(getHistorySnapshot(page, 20)).toEqual({
|
|
165
|
+
hasOlder: false,
|
|
166
|
+
olderCursor: null,
|
|
167
|
+
recentLimit: 20,
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
// getForkedChatTitle
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
describe("getForkedChatTitle", () => {
|
|
176
|
+
test("prefixes non-fork titles", () => {
|
|
177
|
+
expect(getForkedChatTitle("My Chat")).toBe("Fork: My Chat")
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
test("does not double-prefix already-forked titles", () => {
|
|
181
|
+
expect(getForkedChatTitle("Fork: My Chat")).toBe("Fork: My Chat")
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
test("returns default for empty title", () => {
|
|
185
|
+
expect(getForkedChatTitle("")).toBe("Fork: New Chat")
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
test("returns default for whitespace-only title", () => {
|
|
189
|
+
expect(getForkedChatTitle(" ")).toBe("Fork: New Chat")
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
test("trims whitespace before prefixing", () => {
|
|
193
|
+
expect(getForkedChatTitle(" Hello ")).toBe("Fork: Hello")
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
// getReplayEventPriority — spot-check priority buckets
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
200
|
+
describe("getReplayEventPriority", () => {
|
|
201
|
+
function makeEvent(type: string): StoreEvent {
|
|
202
|
+
return { type } as StoreEvent
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
test("project events have priority 0", () => {
|
|
206
|
+
expect(getReplayEventPriority(makeEvent("project_opened"))).toBe(0)
|
|
207
|
+
expect(getReplayEventPriority(makeEvent("sidebar_project_order_set"))).toBe(0)
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
test("chat_created has priority 1", () => {
|
|
211
|
+
expect(getReplayEventPriority(makeEvent("chat_created"))).toBe(1)
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
test("turn_started has priority 5", () => {
|
|
215
|
+
expect(getReplayEventPriority(makeEvent("turn_started"))).toBe(5)
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
test("turn_finished has priority 8", () => {
|
|
219
|
+
expect(getReplayEventPriority(makeEvent("turn_finished"))).toBe(8)
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
test("loop_armed has priority 11", () => {
|
|
223
|
+
expect(getReplayEventPriority(makeEvent("loop_armed"))).toBe(11)
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
test("orch events have priority 5", () => {
|
|
227
|
+
expect(getReplayEventPriority(makeEvent("orch_run_created"))).toBe(5)
|
|
228
|
+
expect(getReplayEventPriority(makeEvent("orch_task_committed"))).toBe(5)
|
|
229
|
+
})
|
|
230
|
+
})
|