@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,365 @@
|
|
|
1
|
+
// Application settings domain types.
|
|
2
|
+
// Extracted from types.ts to keep the barrel lean.
|
|
3
|
+
// All external consumers importing from "../shared/types" continue to work unchanged.
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
AppThemePreference,
|
|
7
|
+
ChatSoundPreference,
|
|
8
|
+
ChatSoundId,
|
|
9
|
+
EditorPreset,
|
|
10
|
+
DefaultProviderPreference,
|
|
11
|
+
LlmProviderKind,
|
|
12
|
+
} from "./core-types"
|
|
13
|
+
import type {
|
|
14
|
+
ChatProviderPreferences,
|
|
15
|
+
ProviderPreference,
|
|
16
|
+
ClaudeModelOptions,
|
|
17
|
+
CodexModelOptions,
|
|
18
|
+
OpenRouterModelOptions,
|
|
19
|
+
CustomModelEntry,
|
|
20
|
+
CustomModelInput,
|
|
21
|
+
CustomModelPatch,
|
|
22
|
+
TextSnippet,
|
|
23
|
+
TextSnippetInput,
|
|
24
|
+
TextSnippetPatch,
|
|
25
|
+
} from "./provider-model-types"
|
|
26
|
+
import type {
|
|
27
|
+
McpServerConfig,
|
|
28
|
+
McpServerInput,
|
|
29
|
+
McpServerPatch,
|
|
30
|
+
McpServerTestResult,
|
|
31
|
+
McpOAuthState,
|
|
32
|
+
} from "./mcp-types"
|
|
33
|
+
import type { Subagent, SubagentInput, SubagentPatch } from "./subagent-types"
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Auth
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
export interface AuthSettings {
|
|
40
|
+
sessionMaxAgeDays: number
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const AUTH_DEFAULTS: AuthSettings = {
|
|
44
|
+
sessionMaxAgeDays: 30,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const AUTH_SESSION_MAX_AGE_DAYS_MIN = 1
|
|
48
|
+
export const AUTH_SESSION_MAX_AGE_DAYS_MAX = 365
|
|
49
|
+
|
|
50
|
+
export type OAuthTokenStatus = "active" | "limited" | "error" | "disabled"
|
|
51
|
+
|
|
52
|
+
export interface OAuthTokenEntry {
|
|
53
|
+
id: string
|
|
54
|
+
label: string
|
|
55
|
+
token: string
|
|
56
|
+
status: OAuthTokenStatus
|
|
57
|
+
limitedUntil: number | null
|
|
58
|
+
lastUsedAt: number | null
|
|
59
|
+
lastErrorAt: number | null
|
|
60
|
+
lastErrorMessage: string | null
|
|
61
|
+
addedAt: number
|
|
62
|
+
// Per-token concurrent-chat cap. When omitted, the pool falls back to
|
|
63
|
+
// ClaudeAuthSettings.concurrencyDefault. Default 1 preserves the
|
|
64
|
+
// historical 1-token-per-chat invariant. Range
|
|
65
|
+
// [OAUTH_TOKEN_MAX_CONCURRENT_MIN, OAUTH_TOKEN_MAX_CONCURRENT_MAX].
|
|
66
|
+
maxConcurrent?: number
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface ClaudeAuthSettings {
|
|
70
|
+
tokens: OAuthTokenEntry[]
|
|
71
|
+
// Pool-wide default applied to tokens whose maxConcurrent is omitted.
|
|
72
|
+
concurrencyDefault: number
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const OAUTH_TOKEN_MAX_CONCURRENT_MIN = 1
|
|
76
|
+
export const OAUTH_TOKEN_MAX_CONCURRENT_MAX = 5
|
|
77
|
+
export const OAUTH_TOKEN_CONCURRENCY_DEFAULT = 1
|
|
78
|
+
|
|
79
|
+
export const CLAUDE_AUTH_DEFAULTS: ClaudeAuthSettings = {
|
|
80
|
+
tokens: [],
|
|
81
|
+
concurrencyDefault: OAUTH_TOKEN_CONCURRENCY_DEFAULT,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const OAUTH_TOKEN_LABEL_MAX = 64
|
|
85
|
+
export const OAUTH_TOKEN_VALUE_MAX = 1024
|
|
86
|
+
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Upload
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
|
|
91
|
+
export interface UploadSettings {
|
|
92
|
+
maxFileSizeMb: number
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const UPLOAD_DEFAULTS: UploadSettings = {
|
|
96
|
+
maxFileSizeMb: 100,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const UPLOAD_MAX_FILE_SIZE_MB_MIN = 1
|
|
100
|
+
export const UPLOAD_MAX_FILE_SIZE_MB_MAX = 2048
|
|
101
|
+
|
|
102
|
+
export const GLOBAL_PROMPT_APPEND_MAX_CHARS = 8_000
|
|
103
|
+
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// Claude driver
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
export type ClaudeDriverPreference = "sdk" | "pty"
|
|
109
|
+
|
|
110
|
+
export const CLAUDE_DRIVER_VALUES: readonly ClaudeDriverPreference[] = ["sdk", "pty"]
|
|
111
|
+
|
|
112
|
+
export function isClaudeDriverPreference(value: string | null | undefined): value is ClaudeDriverPreference {
|
|
113
|
+
return value === "sdk" || value === "pty"
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface ClaudePtyLifecycleSettings {
|
|
117
|
+
idleTimeoutMs: number
|
|
118
|
+
maxConcurrent: number
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const CLAUDE_PTY_LIFECYCLE_DEFAULTS: ClaudePtyLifecycleSettings = {
|
|
122
|
+
idleTimeoutMs: 600_000,
|
|
123
|
+
maxConcurrent: 4,
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export const CLAUDE_PTY_IDLE_TIMEOUT_MS_MIN = 60_000
|
|
127
|
+
export const CLAUDE_PTY_IDLE_TIMEOUT_MS_MAX = 3_600_000
|
|
128
|
+
export const CLAUDE_PTY_MAX_CONCURRENT_MIN = 1
|
|
129
|
+
export const CLAUDE_PTY_MAX_CONCURRENT_MAX = 16
|
|
130
|
+
|
|
131
|
+
export interface ClaudeDriverSettings {
|
|
132
|
+
preference: ClaudeDriverPreference
|
|
133
|
+
lifecycle: ClaudePtyLifecycleSettings
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export const CLAUDE_DRIVER_DEFAULTS: ClaudeDriverSettings = {
|
|
137
|
+
preference: "sdk",
|
|
138
|
+
lifecycle: { ...CLAUDE_PTY_LIFECYCLE_DEFAULTS },
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export type ClaudeSessionLifecycleStatus = "cold" | "warming" | "active" | "idle" | "cooling"
|
|
142
|
+
|
|
143
|
+
export interface ChatSessionStateSnapshot {
|
|
144
|
+
chatId: string
|
|
145
|
+
state: ClaudeSessionLifecycleStatus
|
|
146
|
+
updatedAt: number
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// Cloudflare tunnel
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
export type CloudflareTunnelMode = "always-ask" | "auto-expose"
|
|
154
|
+
|
|
155
|
+
export interface CloudflareTunnelSettings {
|
|
156
|
+
enabled: boolean
|
|
157
|
+
cloudflaredPath: string
|
|
158
|
+
mode: CloudflareTunnelMode
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export const CLOUDFLARE_TUNNEL_DEFAULTS: CloudflareTunnelSettings = {
|
|
162
|
+
enabled: false,
|
|
163
|
+
cloudflaredPath: "cloudflared",
|
|
164
|
+
mode: "always-ask",
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export type CloudflareTunnelState = "proposed" | "active" | "stopped" | "failed"
|
|
168
|
+
|
|
169
|
+
export interface CloudflareTunnelRecord {
|
|
170
|
+
tunnelId: string
|
|
171
|
+
chatId: string
|
|
172
|
+
port: number
|
|
173
|
+
state: CloudflareTunnelState
|
|
174
|
+
url: string | null
|
|
175
|
+
error: string | null
|
|
176
|
+
proposedAt: number
|
|
177
|
+
activatedAt: number | null
|
|
178
|
+
stoppedAt: number | null
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
// Keybindings
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
|
|
185
|
+
export type KeybindingAction =
|
|
186
|
+
| "toggleEmbeddedTerminal"
|
|
187
|
+
| "toggleRightSidebar"
|
|
188
|
+
| "openInFinder"
|
|
189
|
+
| "openInEditor"
|
|
190
|
+
| "addSplitTerminal"
|
|
191
|
+
| "jumpToSidebarChat"
|
|
192
|
+
| "createChatInCurrentProject"
|
|
193
|
+
| "openAddProject"
|
|
194
|
+
| "newStack"
|
|
195
|
+
| "newStackChat"
|
|
196
|
+
| "jumpToStacks"
|
|
197
|
+
|
|
198
|
+
export const DEFAULT_KEYBINDINGS: Record<KeybindingAction, string[]> = {
|
|
199
|
+
toggleEmbeddedTerminal: ["cmd+j", "ctrl+`"],
|
|
200
|
+
toggleRightSidebar: ["cmd+b", "ctrl+b"],
|
|
201
|
+
openInFinder: ["cmd+alt+f", "ctrl+alt+f"],
|
|
202
|
+
openInEditor: ["cmd+shift+o", "ctrl+shift+o"],
|
|
203
|
+
addSplitTerminal: ["cmd+/", "ctrl+/"],
|
|
204
|
+
jumpToSidebarChat: ["cmd+alt"],
|
|
205
|
+
createChatInCurrentProject: ["cmd+alt+n"],
|
|
206
|
+
openAddProject: ["cmd+alt+o"],
|
|
207
|
+
newStack: ["cmd+alt+w"],
|
|
208
|
+
newStackChat: ["cmd+alt+shift+n"],
|
|
209
|
+
jumpToStacks: ["g s"],
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export const KEYBINDING_ACTIONS: readonly KeybindingAction[] = [
|
|
213
|
+
"toggleEmbeddedTerminal",
|
|
214
|
+
"toggleRightSidebar",
|
|
215
|
+
"openInFinder",
|
|
216
|
+
"openInEditor",
|
|
217
|
+
"addSplitTerminal",
|
|
218
|
+
"jumpToSidebarChat",
|
|
219
|
+
"createChatInCurrentProject",
|
|
220
|
+
"openAddProject",
|
|
221
|
+
"newStack",
|
|
222
|
+
"newStackChat",
|
|
223
|
+
"jumpToStacks",
|
|
224
|
+
] satisfies KeybindingAction[]
|
|
225
|
+
|
|
226
|
+
export interface KeybindingsSnapshot {
|
|
227
|
+
bindings: Record<KeybindingAction, string[]>
|
|
228
|
+
warning: string | null
|
|
229
|
+
filePathDisplay: string
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ---------------------------------------------------------------------------
|
|
233
|
+
// App settings snapshot & patch
|
|
234
|
+
// ---------------------------------------------------------------------------
|
|
235
|
+
|
|
236
|
+
export interface AppSettingsSnapshot {
|
|
237
|
+
analyticsEnabled: boolean
|
|
238
|
+
browserSettingsMigrated: boolean
|
|
239
|
+
theme: AppThemePreference
|
|
240
|
+
chatSoundPreference: ChatSoundPreference
|
|
241
|
+
chatSoundId: ChatSoundId
|
|
242
|
+
terminal: {
|
|
243
|
+
scrollbackLines: number
|
|
244
|
+
minColumnWidth: number
|
|
245
|
+
}
|
|
246
|
+
editor: {
|
|
247
|
+
preset: EditorPreset
|
|
248
|
+
commandTemplate: string
|
|
249
|
+
}
|
|
250
|
+
defaultProvider: DefaultProviderPreference
|
|
251
|
+
providerDefaults: ChatProviderPreferences
|
|
252
|
+
warning: string | null
|
|
253
|
+
filePathDisplay: string
|
|
254
|
+
cloudflareTunnel: CloudflareTunnelSettings
|
|
255
|
+
auth: AuthSettings
|
|
256
|
+
claudeAuth: ClaudeAuthSettings
|
|
257
|
+
uploads: UploadSettings
|
|
258
|
+
subagents: Subagent[]
|
|
259
|
+
customMcpServers: McpServerConfig[]
|
|
260
|
+
customModels: CustomModelEntry[]
|
|
261
|
+
textSnippets: TextSnippet[]
|
|
262
|
+
claudeDriver: ClaudeDriverSettings
|
|
263
|
+
globalPromptAppend: string
|
|
264
|
+
shareDefaultTtlHours: number
|
|
265
|
+
subagentRuntime: SubagentRuntimeSettings
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Runtime knobs for delegated subagent runs (delegate_subagent) and the
|
|
270
|
+
* autonomous loop (setup_loop). `runTimeoutMs` is the stall/idle watchdog
|
|
271
|
+
* window — a run is aborted only after this long with NO streamed activity,
|
|
272
|
+
* not a total wall-clock cap. `defaultLoopSubagentId` is the subagent
|
|
273
|
+
* setup_loop delegates to when the caller omits an explicit id.
|
|
274
|
+
*/
|
|
275
|
+
export interface SubagentRuntimeSettings {
|
|
276
|
+
runTimeoutMs: number
|
|
277
|
+
defaultLoopSubagentId: string | null
|
|
278
|
+
/**
|
|
279
|
+
* Subagent an orchestration run (orch_run) delegates each phase to when the
|
|
280
|
+
* caller omits an explicit id. Optional: absent = no default (the caller must
|
|
281
|
+
* pass `subagentId`). Full settings CRUD/UI is a later phase.
|
|
282
|
+
*/
|
|
283
|
+
defaultOrchSubagentId?: string | null
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export interface AppSettingsPatch {
|
|
287
|
+
analyticsEnabled?: boolean
|
|
288
|
+
browserSettingsMigrated?: boolean
|
|
289
|
+
theme?: AppThemePreference
|
|
290
|
+
chatSoundPreference?: ChatSoundPreference
|
|
291
|
+
chatSoundId?: ChatSoundId
|
|
292
|
+
terminal?: Partial<AppSettingsSnapshot["terminal"]>
|
|
293
|
+
editor?: Partial<AppSettingsSnapshot["editor"]>
|
|
294
|
+
defaultProvider?: DefaultProviderPreference
|
|
295
|
+
providerDefaults?: {
|
|
296
|
+
claude?: Partial<ProviderPreference<ClaudeModelOptions>>
|
|
297
|
+
codex?: Partial<ProviderPreference<CodexModelOptions>>
|
|
298
|
+
openrouter?: Partial<ProviderPreference<OpenRouterModelOptions>>
|
|
299
|
+
}
|
|
300
|
+
cloudflareTunnel?: Partial<CloudflareTunnelSettings>
|
|
301
|
+
auth?: Partial<AuthSettings>
|
|
302
|
+
claudeAuth?: Partial<ClaudeAuthSettings>
|
|
303
|
+
uploads?: Partial<UploadSettings>
|
|
304
|
+
subagents?: {
|
|
305
|
+
create?: SubagentInput
|
|
306
|
+
update?: { id: string; patch: SubagentPatch }
|
|
307
|
+
delete?: { id: string }
|
|
308
|
+
}
|
|
309
|
+
customMcpServers?: {
|
|
310
|
+
create?: McpServerInput
|
|
311
|
+
update?: { id: string; patch: McpServerPatch }
|
|
312
|
+
delete?: { id: string }
|
|
313
|
+
setEnabled?: { id: string; enabled: boolean }
|
|
314
|
+
setTestResult?: { id: string; result: McpServerTestResult }
|
|
315
|
+
setOAuthState?: { id: string; oauth: McpOAuthState }
|
|
316
|
+
}
|
|
317
|
+
customModels?: {
|
|
318
|
+
create?: CustomModelInput
|
|
319
|
+
update?: { id: string; patch: CustomModelPatch }
|
|
320
|
+
delete?: { id: string }
|
|
321
|
+
}
|
|
322
|
+
textSnippets?: {
|
|
323
|
+
create?: TextSnippetInput
|
|
324
|
+
update?: { id: string; patch: TextSnippetPatch }
|
|
325
|
+
delete?: { id: string }
|
|
326
|
+
}
|
|
327
|
+
claudeDriver?: {
|
|
328
|
+
preference?: ClaudeDriverPreference
|
|
329
|
+
lifecycle?: Partial<ClaudePtyLifecycleSettings>
|
|
330
|
+
}
|
|
331
|
+
globalPromptAppend?: string
|
|
332
|
+
shareDefaultTtlHours?: number
|
|
333
|
+
subagentRuntime?: Partial<SubagentRuntimeSettings>
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// ---------------------------------------------------------------------------
|
|
337
|
+
// Type guards
|
|
338
|
+
// ---------------------------------------------------------------------------
|
|
339
|
+
|
|
340
|
+
export function isEditorPreset(value: string): value is EditorPreset {
|
|
341
|
+
return value === "cursor" || value === "vscode" || value === "xcode" || value === "windsurf" || value === "custom"
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export function isChatSoundPreference(value: string): value is ChatSoundPreference {
|
|
345
|
+
return value === "never" || value === "unfocused" || value === "always"
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export function isChatSoundId(value: string): value is ChatSoundId {
|
|
349
|
+
return (
|
|
350
|
+
value === "blow" || value === "bottle" || value === "frog" || value === "funk" ||
|
|
351
|
+
value === "glass" || value === "ping" || value === "pop" || value === "purr" || value === "tink"
|
|
352
|
+
)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export function isCloudFlareTunnelMode(value: string): value is CloudflareTunnelMode {
|
|
356
|
+
return value === "always-ask" || value === "auto-expose"
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export function isLlmProviderKind(value: string): value is LlmProviderKind {
|
|
360
|
+
return value === "openai" || value === "openrouter" || value === "custom"
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export function isAppThemePreference(value: string): value is AppThemePreference {
|
|
364
|
+
return value === "light" || value === "dark" || value === "system"
|
|
365
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Core primitive types shared across all domain modules.
|
|
2
|
+
// Extracted from types.ts to break circular-import chains between
|
|
3
|
+
// domain-specific type files (e.g. app-settings-types, subagent-types).
|
|
4
|
+
//
|
|
5
|
+
// Do NOT add complex interfaces here — keep this file to simple
|
|
6
|
+
// union types, string-literal unions, and small constants.
|
|
7
|
+
|
|
8
|
+
export const STORE_VERSION = 3 as const
|
|
9
|
+
export const PROTOCOL_VERSION = 1 as const
|
|
10
|
+
|
|
11
|
+
export type AgentProvider = "claude" | "codex" | "openrouter"
|
|
12
|
+
export type LlmProviderKind = "openai" | "openrouter" | "custom"
|
|
13
|
+
export type AppThemePreference = "light" | "dark" | "system"
|
|
14
|
+
export type ChatSoundPreference = "never" | "unfocused" | "always"
|
|
15
|
+
export type ChatSoundId = "blow" | "bottle" | "frog" | "funk" | "glass" | "ping" | "pop" | "purr" | "tink"
|
|
16
|
+
export type DefaultProviderPreference = "last_used" | AgentProvider
|
|
17
|
+
export type EditorPreset = "cursor" | "vscode" | "xcode" | "windsurf" | "custom"
|
|
18
|
+
export const DEFAULT_OPENAI_SDK_MODEL = "gpt-5.4-mini"
|
|
19
|
+
|
|
20
|
+
export type AttachmentKind = "image" | "file" | "mention"
|
|
21
|
+
|
|
22
|
+
export type KannaStatus =
|
|
23
|
+
| "idle"
|
|
24
|
+
| "starting"
|
|
25
|
+
| "running"
|
|
26
|
+
| "waiting_for_user"
|
|
27
|
+
| "failed"
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// Git diff, branch, and worktree types.
|
|
2
|
+
// Extracted from types.ts to keep the barrel lean.
|
|
3
|
+
// All external consumers importing from "../shared/types" continue to work unchanged.
|
|
4
|
+
|
|
5
|
+
export interface ChatDiffFile {
|
|
6
|
+
path: string
|
|
7
|
+
changeType: "added" | "deleted" | "modified" | "renamed"
|
|
8
|
+
isUntracked: boolean
|
|
9
|
+
additions: number
|
|
10
|
+
deletions: number
|
|
11
|
+
patchDigest: string
|
|
12
|
+
mimeType?: string
|
|
13
|
+
size?: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ChatBranchHistoryEntry {
|
|
17
|
+
sha: string
|
|
18
|
+
summary: string
|
|
19
|
+
description: string
|
|
20
|
+
authorName?: string
|
|
21
|
+
authoredAt: string
|
|
22
|
+
tags: string[]
|
|
23
|
+
githubUrl?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ChatBranchHistorySnapshot {
|
|
27
|
+
entries: ChatBranchHistoryEntry[]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type ChatBranchListEntryKind = "local" | "remote" | "pull_request"
|
|
31
|
+
|
|
32
|
+
export interface ChatBranchListEntry {
|
|
33
|
+
id: string
|
|
34
|
+
kind: ChatBranchListEntryKind
|
|
35
|
+
name: string
|
|
36
|
+
displayName: string
|
|
37
|
+
updatedAt?: string
|
|
38
|
+
description?: string
|
|
39
|
+
remoteRef?: string
|
|
40
|
+
prNumber?: number
|
|
41
|
+
prTitle?: string
|
|
42
|
+
headRefName?: string
|
|
43
|
+
headLabel?: string
|
|
44
|
+
headRepoCloneUrl?: string
|
|
45
|
+
isCrossRepository?: boolean
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ChatBranchListResult {
|
|
49
|
+
currentBranchName?: string
|
|
50
|
+
defaultBranchName?: string
|
|
51
|
+
recent: ChatBranchListEntry[]
|
|
52
|
+
local: ChatBranchListEntry[]
|
|
53
|
+
remote: ChatBranchListEntry[]
|
|
54
|
+
pullRequests: ChatBranchListEntry[]
|
|
55
|
+
pullRequestsStatus: "available" | "unavailable" | "error"
|
|
56
|
+
pullRequestsError?: string
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface GitHubPublishInfo {
|
|
60
|
+
ghInstalled: boolean
|
|
61
|
+
authenticated: boolean
|
|
62
|
+
activeAccountLogin?: string
|
|
63
|
+
owners: string[]
|
|
64
|
+
suggestedRepoName: string
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface GitHubRepoAvailabilityResult {
|
|
68
|
+
available: boolean
|
|
69
|
+
message: string
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface BranchMetadata {
|
|
73
|
+
branchName?: string
|
|
74
|
+
defaultBranchName?: string
|
|
75
|
+
hasOriginRemote?: boolean
|
|
76
|
+
originRepoSlug?: string
|
|
77
|
+
hasUpstream?: boolean
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface UpstreamStatus {
|
|
81
|
+
aheadCount?: number
|
|
82
|
+
behindCount?: number
|
|
83
|
+
lastFetchedAt?: string
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface ChatDiffSnapshot extends BranchMetadata, UpstreamStatus {
|
|
87
|
+
status: "unknown" | "ready" | "no_repo"
|
|
88
|
+
files: ChatDiffFile[]
|
|
89
|
+
branchHistory?: ChatBranchHistorySnapshot
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface BranchActionSuccess {
|
|
93
|
+
ok: true
|
|
94
|
+
branchName?: string
|
|
95
|
+
snapshotChanged: boolean
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface BranchActionFailure {
|
|
99
|
+
ok: false
|
|
100
|
+
title: string
|
|
101
|
+
message: string
|
|
102
|
+
detail?: string
|
|
103
|
+
cancelled?: boolean
|
|
104
|
+
snapshotChanged?: boolean
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export type ChatSyncSuccess = BranchActionSuccess & {
|
|
108
|
+
action: "fetch" | "pull" | "push" | "publish"
|
|
109
|
+
aheadCount?: number
|
|
110
|
+
behindCount?: number
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export type ChatSyncFailure = BranchActionFailure & {
|
|
114
|
+
action: "fetch" | "pull" | "push" | "publish"
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export type ChatSyncResult = ChatSyncSuccess | ChatSyncFailure
|
|
118
|
+
|
|
119
|
+
export type DiffCommitMode = "commit_and_push" | "commit_only"
|
|
120
|
+
|
|
121
|
+
export type ChatCheckoutBranchSuccess = BranchActionSuccess
|
|
122
|
+
export type ChatCheckoutBranchFailure = BranchActionFailure
|
|
123
|
+
export type ChatCheckoutBranchResult = ChatCheckoutBranchSuccess | ChatCheckoutBranchFailure
|
|
124
|
+
|
|
125
|
+
export type ChatCreateBranchSuccess = BranchActionSuccess & { branchName: string }
|
|
126
|
+
export type ChatCreateBranchFailure = BranchActionFailure
|
|
127
|
+
export type ChatCreateBranchResult = ChatCreateBranchSuccess | ChatCreateBranchFailure
|
|
128
|
+
|
|
129
|
+
export type ChatMergePreviewStatus = "up_to_date" | "mergeable" | "conflicts" | "error"
|
|
130
|
+
|
|
131
|
+
export interface ChatMergePreviewResult {
|
|
132
|
+
currentBranchName?: string
|
|
133
|
+
targetBranchName: string
|
|
134
|
+
targetDisplayName: string
|
|
135
|
+
status: ChatMergePreviewStatus
|
|
136
|
+
commitCount: number
|
|
137
|
+
hasConflicts: boolean
|
|
138
|
+
message: string
|
|
139
|
+
detail?: string
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export type ChatMergeBranchSuccess = BranchActionSuccess
|
|
143
|
+
export type ChatMergeBranchFailure = BranchActionFailure
|
|
144
|
+
export type ChatMergeBranchResult = ChatMergeBranchSuccess | ChatMergeBranchFailure
|
|
145
|
+
|
|
146
|
+
export type DiffCommitSuccess = BranchActionSuccess & {
|
|
147
|
+
mode: DiffCommitMode
|
|
148
|
+
pushed: boolean
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export type DiffCommitFailure = BranchActionFailure & {
|
|
152
|
+
mode: DiffCommitMode
|
|
153
|
+
phase: "commit" | "push"
|
|
154
|
+
localCommitCreated?: boolean
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export type DiffCommitResult = DiffCommitSuccess | DiffCommitFailure
|
|
158
|
+
|
|
159
|
+
export interface GitWorktree {
|
|
160
|
+
path: string // absolute
|
|
161
|
+
branch: string // e.g. "main", "feat/x", "(detached)"
|
|
162
|
+
sha: string // HEAD commit sha
|
|
163
|
+
isPrimary: boolean
|
|
164
|
+
isLocked: boolean // git has flagged this worktree as locked (pruning inhibited)
|
|
165
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import { buildLoopProgress, deriveChunkLabel } from "./loop-progress"
|
|
3
|
+
import type { SubagentRunSnapshot } from "./types"
|
|
4
|
+
|
|
5
|
+
function run(overrides: Partial<SubagentRunSnapshot>): SubagentRunSnapshot {
|
|
6
|
+
return {
|
|
7
|
+
runId: "run-1",
|
|
8
|
+
chatId: "c1",
|
|
9
|
+
subagentId: "sa-1",
|
|
10
|
+
subagentName: "subagent-general",
|
|
11
|
+
label: null,
|
|
12
|
+
provider: "claude",
|
|
13
|
+
model: "claude-sonnet-4-6",
|
|
14
|
+
status: "running",
|
|
15
|
+
parentUserMessageId: "m1",
|
|
16
|
+
parentRunId: null,
|
|
17
|
+
depth: 0,
|
|
18
|
+
startedAt: 1_000,
|
|
19
|
+
finishedAt: null,
|
|
20
|
+
finalText: null,
|
|
21
|
+
error: null,
|
|
22
|
+
usage: null,
|
|
23
|
+
entries: [],
|
|
24
|
+
pendingTool: null,
|
|
25
|
+
...overrides,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe("deriveChunkLabel", () => {
|
|
30
|
+
test("first non-blank line, trimmed", () => {
|
|
31
|
+
expect(deriveChunkLabel("\n\n Migrate useKannaState.ts \nmore detail")).toBe(
|
|
32
|
+
"Migrate useKannaState.ts",
|
|
33
|
+
)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test("strips a single leading markdown marker", () => {
|
|
37
|
+
expect(deriveChunkLabel("- Fix Dockerfiles for Berry")).toBe("Fix Dockerfiles for Berry")
|
|
38
|
+
expect(deriveChunkLabel("## Build + start full stack")).toBe("Build + start full stack")
|
|
39
|
+
expect(deriveChunkLabel("3. Verify service health")).toBe("Verify service health")
|
|
40
|
+
expect(deriveChunkLabel("> quoted chunk")).toBe("quoted chunk")
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test("does not mistake mid-line content for a marker", () => {
|
|
44
|
+
expect(deriveChunkLabel("3D rendering pipeline")).toBe("3D rendering pipeline")
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test("caps overlong labels with an ellipsis", () => {
|
|
48
|
+
const long = "x".repeat(200)
|
|
49
|
+
const label = deriveChunkLabel(long)
|
|
50
|
+
expect(label.length).toBe(80)
|
|
51
|
+
expect(label.endsWith("…")).toBe(true)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test("empty prompt → empty string", () => {
|
|
55
|
+
expect(deriveChunkLabel(" \n ")).toBe("")
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
describe("buildLoopProgress", () => {
|
|
60
|
+
test("maps run status → row status and prefers label over subagentName", () => {
|
|
61
|
+
const snapshot = buildLoopProgress({
|
|
62
|
+
chatId: "c1",
|
|
63
|
+
armed: true,
|
|
64
|
+
loopArmedAt: 0,
|
|
65
|
+
rateLimit: null,
|
|
66
|
+
runs: [
|
|
67
|
+
run({ runId: "r1", status: "completed", label: "chunk one", startedAt: 10, finishedAt: 20 }),
|
|
68
|
+
run({ runId: "r2", status: "running", label: null, subagentName: "worker", startedAt: 30 }),
|
|
69
|
+
run({ runId: "r3", status: "failed", label: "chunk three", startedAt: 40 }),
|
|
70
|
+
],
|
|
71
|
+
})
|
|
72
|
+
expect(snapshot.rows.map((r) => [r.runId, r.status, r.label])).toEqual([
|
|
73
|
+
["r3", "failed", "chunk three"],
|
|
74
|
+
["r2", "running", "worker"], // label fallback to subagentName
|
|
75
|
+
["r1", "done", "chunk one"],
|
|
76
|
+
])
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test("excludes nested sub-spawns and runs started before the loop armed", () => {
|
|
80
|
+
const snapshot = buildLoopProgress({
|
|
81
|
+
chatId: "c1",
|
|
82
|
+
armed: true,
|
|
83
|
+
loopArmedAt: 100,
|
|
84
|
+
rateLimit: null,
|
|
85
|
+
runs: [
|
|
86
|
+
run({ runId: "pre", startedAt: 50 }), // before arm → excluded
|
|
87
|
+
run({ runId: "nested", startedAt: 150, depth: 1 }), // sub-spawn → excluded
|
|
88
|
+
run({ runId: "keep", startedAt: 200 }),
|
|
89
|
+
],
|
|
90
|
+
})
|
|
91
|
+
expect(snapshot.rows.map((r) => r.runId)).toEqual(["keep"])
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test("passes rate-limit through and reflects armed flag", () => {
|
|
95
|
+
const snapshot = buildLoopProgress({
|
|
96
|
+
chatId: "c1",
|
|
97
|
+
armed: false,
|
|
98
|
+
loopArmedAt: null,
|
|
99
|
+
rateLimit: { scheduleId: "s1", resetAt: 123, tz: "Asia/Saigon", scheduled: false },
|
|
100
|
+
runs: [],
|
|
101
|
+
})
|
|
102
|
+
expect(snapshot.armed).toBe(false)
|
|
103
|
+
expect(snapshot.rateLimit).toEqual({ scheduleId: "s1", resetAt: 123, tz: "Asia/Saigon", scheduled: false })
|
|
104
|
+
expect(snapshot.rows).toEqual([])
|
|
105
|
+
})
|
|
106
|
+
})
|