@cuongtran001/kanna 1.2.0 → 1.3.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.
Files changed (67) hide show
  1. package/dist/client/assets/{arc-BLk1CZKB.js → arc-CAysfmX5.js} +1 -1
  2. package/dist/client/assets/{architectureDiagram-3BPJPVTR-B01ZQYte.js → architectureDiagram-3BPJPVTR-BKqRYsm4.js} +1 -1
  3. package/dist/client/assets/{blockDiagram-GPEHLZMM-diL6a3cq.js → blockDiagram-GPEHLZMM-Ce7Eorec.js} +1 -1
  4. package/dist/client/assets/{c4Diagram-AAUBKEIU-BY1verL8.js → c4Diagram-AAUBKEIU-CJ77zauH.js} +1 -1
  5. package/dist/client/assets/channel-KQhRwDzJ.js +1 -0
  6. package/dist/client/assets/{chunk-2J33WTMH-psdZuajw.js → chunk-2J33WTMH-Rk_3zLxJ.js} +1 -1
  7. package/dist/client/assets/{chunk-4BX2VUAB-CrwQVBc5.js → chunk-4BX2VUAB-DPIz6Nvf.js} +1 -1
  8. package/dist/client/assets/{chunk-55IACEB6-9-aj4jKO.js → chunk-55IACEB6-D-TkbGh2.js} +1 -1
  9. package/dist/client/assets/{chunk-727SXJPM-1FNK9_nB.js → chunk-727SXJPM-B4NOSIZV.js} +1 -1
  10. package/dist/client/assets/{chunk-AQP2D5EJ-DFhvd_Ek.js → chunk-AQP2D5EJ-Cm_TGLYm.js} +1 -1
  11. package/dist/client/assets/{chunk-FMBD7UC4-B7MLrDa7.js → chunk-FMBD7UC4-Bu45BUVh.js} +1 -1
  12. package/dist/client/assets/{chunk-ND2GUHAM-D4Df05QE.js → chunk-ND2GUHAM-C1Bw8_gg.js} +1 -1
  13. package/dist/client/assets/{chunk-QZHKN3VN-Iq17Dznm.js → chunk-QZHKN3VN-DueG15o0.js} +1 -1
  14. package/dist/client/assets/classDiagram-4FO5ZUOK-ChxT1GNq.js +1 -0
  15. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-ChxT1GNq.js +1 -0
  16. package/dist/client/assets/{cose-bilkent-S5V4N54A-BmaZhtXy.js → cose-bilkent-S5V4N54A-YgpMurXf.js} +1 -1
  17. package/dist/client/assets/{dagre-BM42HDAG-BCmpfnfP.js → dagre-BM42HDAG-Dop7Oq67.js} +1 -1
  18. package/dist/client/assets/{diagram-2AECGRRQ-BIEcKowS.js → diagram-2AECGRRQ-unMfBEJH.js} +1 -1
  19. package/dist/client/assets/{diagram-5GNKFQAL-CRBABBfv.js → diagram-5GNKFQAL-D9EJ9qMI.js} +1 -1
  20. package/dist/client/assets/{diagram-KO2AKTUF-BRxUk16N.js → diagram-KO2AKTUF-C5IykOIR.js} +1 -1
  21. package/dist/client/assets/{diagram-LMA3HP47-DPBs4xl1.js → diagram-LMA3HP47-DJAuPngz.js} +1 -1
  22. package/dist/client/assets/{diagram-OG6HWLK6-BE9rgVv8.js → diagram-OG6HWLK6-B3wHFtS-.js} +1 -1
  23. package/dist/client/assets/{erDiagram-TEJ5UH35-D3vgA1xJ.js → erDiagram-TEJ5UH35-Dm7kt9o5.js} +1 -1
  24. package/dist/client/assets/{flowDiagram-I6XJVG4X-Cgu-8XXC.js → flowDiagram-I6XJVG4X-s9Lds3ny.js} +1 -1
  25. package/dist/client/assets/{ganttDiagram-6RSMTGT7-CgjXVyTR.js → ganttDiagram-6RSMTGT7-CRQNYdAf.js} +1 -1
  26. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-zD5D8B-i.js → gitGraphDiagram-PVQCEYII-DhYjK4Jb.js} +1 -1
  27. package/dist/client/assets/{index-zQLLeAfl.js → index-C0U2kwuB.js} +253 -243
  28. package/dist/client/assets/index-CLsOWXff.css +32 -0
  29. package/dist/client/assets/{index-DBTdhMbJ.js → index-CbSY9HfS.js} +1 -1
  30. package/dist/client/assets/{infoDiagram-5YYISTIA-BaFHqT4u.js → infoDiagram-5YYISTIA-CRKJ0PSH.js} +1 -1
  31. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-2yAC1br2.js → ishikawaDiagram-YF4QCWOH-CEAkB0IY.js} +1 -1
  32. package/dist/client/assets/{journeyDiagram-JHISSGLW-DacQ-ND5.js → journeyDiagram-JHISSGLW-CyOK5_DG.js} +1 -1
  33. package/dist/client/assets/{kanban-definition-UN3LZRKU-Qlbwm3z4.js → kanban-definition-UN3LZRKU-BfKjiJSo.js} +1 -1
  34. package/dist/client/assets/{linear-DeafqpgO.js → linear-DLR6miy3.js} +1 -1
  35. package/dist/client/assets/{mermaid.core-KYiCPJDj.js → mermaid.core-D7AdC_lE.js} +4 -4
  36. package/dist/client/assets/{mindmap-definition-RKZ34NQL-B1V-T6d3.js → mindmap-definition-RKZ34NQL-zYyJtDSa.js} +1 -1
  37. package/dist/client/assets/{pieDiagram-4H26LBE5-DOF9OFRu.js → pieDiagram-4H26LBE5-BgP8PFlo.js} +1 -1
  38. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-lH1mB7ko.js → quadrantDiagram-W4KKPZXB-Di8h7J96.js} +1 -1
  39. package/dist/client/assets/{requirementDiagram-4Y6WPE33-BaTsJ_sS.js → requirementDiagram-4Y6WPE33-DuTzFl3R.js} +1 -1
  40. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-Pfxib5iI.js → sankeyDiagram-5OEKKPKP-DBRoMm_T.js} +1 -1
  41. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BQMEKkBC.js → sequenceDiagram-3UESZ5HK-Bmb-1HUN.js} +1 -1
  42. package/dist/client/assets/{stateDiagram-AJRCARHV-CuldNnzp.js → stateDiagram-AJRCARHV-fnv5ayn-.js} +1 -1
  43. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-D4X-ZnLV.js +1 -0
  44. package/dist/client/assets/{timeline-definition-PNZ67QCA-DP7u0YAg.js → timeline-definition-PNZ67QCA-ByWRdpQX.js} +1 -1
  45. package/dist/client/assets/{vennDiagram-CIIHVFJN-QlrjpNeQ.js → vennDiagram-CIIHVFJN--2WGpVXW.js} +1 -1
  46. package/dist/client/assets/{wardley-L42UT6IY-BqdJ_sAM.js → wardley-L42UT6IY-24zlnzxK.js} +1 -1
  47. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-Wi3kaQ5X.js → wardleyDiagram-YWT4CUSO-Cllvqb3K.js} +1 -1
  48. package/dist/client/assets/{xychartDiagram-2RQKCTM6-D-KXMyCG.js → xychartDiagram-2RQKCTM6-ZcdC6nPE.js} +1 -1
  49. package/dist/client/index.html +2 -2
  50. package/package.json +1 -1
  51. package/src/server/agent.oauth-rotation.test.ts +101 -0
  52. package/src/server/agent.test.ts +3 -3
  53. package/src/server/agent.ts +24 -0
  54. package/src/server/auto-continue/read-model.test.ts +2 -2
  55. package/src/server/auto-continue/read-model.ts +4 -1
  56. package/src/server/event-store.ts +1 -0
  57. package/src/server/events.ts +3 -0
  58. package/src/server/read-models.ts +26 -1
  59. package/src/server/subagent-orchestrator.ts +3 -0
  60. package/src/shared/loop-progress.test.ts +106 -0
  61. package/src/shared/loop-progress.ts +88 -0
  62. package/src/shared/types.ts +45 -0
  63. package/dist/client/assets/channel-BMkhjiLI.js +0 -1
  64. package/dist/client/assets/classDiagram-4FO5ZUOK-C5Kg1XOD.js +0 -1
  65. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-C5Kg1XOD.js +0 -1
  66. package/dist/client/assets/index-H6C-f2Vp.css +0 -32
  67. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Ca2U11af.js +0 -1
@@ -10,6 +10,9 @@ export interface ChatSchedulesProjection {
10
10
  export interface LoopState {
11
11
  subagentId: string
12
12
  prompt: string
13
+ /** Timestamp of the `loop_armed` event that armed the current loop. Used by
14
+ * the Loop Progress panel to exclude delegations from before the arm. */
15
+ armedAt: number
13
16
  }
14
17
 
15
18
  /**
@@ -27,7 +30,7 @@ export function deriveLoopState(
27
30
  for (const event of events) {
28
31
  if (event.chatId !== chatId) continue
29
32
  if (event.kind === "loop_armed") {
30
- state = { subagentId: event.subagentId, prompt: event.prompt }
33
+ state = { subagentId: event.subagentId, prompt: event.prompt, armedAt: event.timestamp }
31
34
  } else if (event.kind === "loop_disarmed") {
32
35
  state = null
33
36
  }
@@ -897,6 +897,7 @@ export class EventStore implements PushEventStore {
897
897
  chatId: e.chatId,
898
898
  subagentId: e.subagentId,
899
899
  subagentName: e.subagentName,
900
+ label: e.label ?? null,
900
901
  provider: e.provider,
901
902
  model: e.model,
902
903
  status: "running",
@@ -335,6 +335,9 @@ export type SubagentRunEvent =
335
335
  runId: string
336
336
  subagentId: string | null
337
337
  subagentName: string
338
+ /** Short prompt-derived label (see SubagentRunSnapshot.label). Optional for
339
+ * back-compat: older events and error paths omit it. */
340
+ label?: string
338
341
  provider: AgentProvider
339
342
  model: string
340
343
  parentUserMessageId: string
@@ -7,16 +7,18 @@ import type {
7
7
  CustomModelEntry,
8
8
  KannaStatus,
9
9
  LocalProjectsSnapshot,
10
+ LoopRateLimitInfo,
10
11
  SidebarChatRow,
11
12
  SidebarData,
12
13
  SidebarProjectGroup,
13
14
  StackSummary,
14
15
  } from "../shared/types"
15
16
  import { mergeCustomModels } from "../shared/types"
17
+ import { buildLoopProgress } from "../shared/loop-progress"
16
18
  import type { ChatRecord, ChatTimingState, StoreState } from "./events"
17
19
  import { resolveLocalPath } from "./paths"
18
20
  import { SERVER_PROVIDERS } from "./provider-catalog"
19
- import { deriveChatSchedules } from "./auto-continue/read-model"
21
+ import { deriveChatSchedules, deriveLoopState } from "./auto-continue/read-model"
20
22
  import { deriveChatTunnels } from "./cloudflare-tunnel/read-model"
21
23
  import type { CloudflareTunnelEvent } from "./cloudflare-tunnel/events"
22
24
 
@@ -336,6 +338,28 @@ export function deriveChatSnapshot(
336
338
  )
337
339
  : {}
338
340
 
341
+ const loopState = deriveLoopState(autoContinueEvents, chat.id)
342
+ const liveSchedule = liveScheduleId ? schedules[liveScheduleId] : undefined
343
+ const rateLimit: LoopRateLimitInfo | null =
344
+ loopState
345
+ && liveSchedule
346
+ && (liveSchedule.state === "proposed" || liveSchedule.state === "scheduled")
347
+ && liveSchedule.resetAt != null
348
+ ? {
349
+ scheduleId: liveSchedule.scheduleId,
350
+ resetAt: liveSchedule.resetAt,
351
+ tz: liveSchedule.tz ?? "system",
352
+ scheduled: liveSchedule.state === "scheduled",
353
+ }
354
+ : null
355
+ const loopProgress = buildLoopProgress({
356
+ chatId: chat.id,
357
+ armed: loopState !== null,
358
+ loopArmedAt: loopState?.armedAt ?? null,
359
+ runs: subagentRunsMap ? [...subagentRunsMap.values()] : [],
360
+ rateLimit,
361
+ })
362
+
339
363
  return {
340
364
  runtime,
341
365
  queuedMessages: (state.queuedMessagesByChatId.get(chat.id) ?? []).map((entry) => ({
@@ -352,6 +376,7 @@ export function deriveChatSnapshot(
352
376
  tunnels,
353
377
  liveTunnelId,
354
378
  subagentRuns,
379
+ loopProgress,
355
380
  ...(resolvedBindings !== undefined ? { resolvedBindings } : {}),
356
381
  }
357
382
  }
@@ -1,6 +1,7 @@
1
1
  import crypto from "node:crypto"
2
2
  import { LOG_PREFIX } from "../shared/branding"
3
3
  import { log } from "../shared/log"
4
+ import { deriveChunkLabel } from "../shared/loop-progress"
4
5
  import type {
5
6
  AgentProvider,
6
7
  ProviderUsage,
@@ -780,6 +781,7 @@ export class SubagentOrchestrator {
780
781
  runId?: string
781
782
  }): Promise<DelegationOutcome> {
782
783
  const runId = args.runId ?? crypto.randomUUID()
784
+ const label = deriveChunkLabel(args.userInstruction)
783
785
  await this.deps.store.appendSubagentEvent({
784
786
  v: 3,
785
787
  type: "subagent_run_started",
@@ -788,6 +790,7 @@ export class SubagentOrchestrator {
788
790
  runId,
789
791
  subagentId: args.subagent.id,
790
792
  subagentName: args.subagent.name,
793
+ ...(label ? { label } : {}),
791
794
  provider: args.subagent.provider,
792
795
  model: args.subagent.model,
793
796
  parentUserMessageId: args.parentUserMessageId,
@@ -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
+ })
@@ -0,0 +1,88 @@
1
+ import type {
2
+ LoopProgressSnapshot,
3
+ LoopRateLimitInfo,
4
+ LoopRow,
5
+ LoopRowStatus,
6
+ SubagentRunSnapshot,
7
+ } from "./types"
8
+
9
+ /**
10
+ * Loop Progress — a per-chat, read-only view of an armed autonomous loop's
11
+ * work, surfaced as a checklist panel (mockup: a "Progress" card with one row
12
+ * per chunk). Event-sourced per round: each background subagent delegation the
13
+ * loop fires becomes one row, its status driven by the existing subagent-run
14
+ * lifecycle events. A rate-limited loop (see the rate-limit-resilience fix)
15
+ * surfaces a distinct "resume" affordance instead of looking silently stuck.
16
+ *
17
+ * These helpers are pure so the server read-model and the client share one
18
+ * shape; the DTO types live in ./types to avoid a runtime import cycle.
19
+ */
20
+
21
+ const MAX_LABEL = 80
22
+
23
+ /**
24
+ * First non-blank line of a spawn prompt, stripped of a single leading markdown
25
+ * marker (heading / list / quote / ordinal) and capped to a chip-friendly
26
+ * length. Deterministic and side-effect free so it can run at delegate time on
27
+ * the server and be asserted in unit tests.
28
+ */
29
+ export function deriveChunkLabel(prompt: string): string {
30
+ const firstLine =
31
+ prompt
32
+ .split("\n")
33
+ .map((line) => line.trim())
34
+ .find((line) => line.length > 0) ?? ""
35
+ const stripped = firstLine.replace(/^(#{1,6}\s+|[-*+]\s+|\d+[.)]\s+|>\s+)/, "").trim()
36
+ const cleaned = stripped.length > 0 ? stripped : firstLine
37
+ if (cleaned.length <= MAX_LABEL) return cleaned
38
+ return `${cleaned.slice(0, MAX_LABEL - 1).trimEnd()}…`
39
+ }
40
+
41
+ function rowStatusFor(run: SubagentRunSnapshot): LoopRowStatus {
42
+ switch (run.status) {
43
+ case "running":
44
+ return "running"
45
+ case "completed":
46
+ return "done"
47
+ default:
48
+ // failed | cancelled | any future terminal-failure state
49
+ return "failed"
50
+ }
51
+ }
52
+
53
+ export interface BuildLoopProgressInput {
54
+ chatId: string
55
+ armed: boolean
56
+ /** Timestamp of the current `loop_armed` event; rows before it are excluded. */
57
+ loopArmedAt: number | null
58
+ /** All subagent runs for the chat (any order). */
59
+ runs: readonly SubagentRunSnapshot[]
60
+ rateLimit: LoopRateLimitInfo | null
61
+ }
62
+
63
+ /**
64
+ * Fold the chat's subagent runs + loop/rate-limit state into the panel DTO.
65
+ * Only top-level (depth 0) runs started since the loop was armed count as loop
66
+ * rows — nested sub-spawns and pre-loop delegations are excluded. Rows are
67
+ * sorted newest-first.
68
+ */
69
+ export function buildLoopProgress(input: BuildLoopProgressInput): LoopProgressSnapshot {
70
+ const since = input.loopArmedAt ?? 0
71
+ const rows: LoopRow[] = input.runs
72
+ .filter((run) => run.depth === 0 && run.startedAt >= since)
73
+ .map((run) => ({
74
+ runId: run.runId,
75
+ label: run.label ?? run.subagentName,
76
+ status: rowStatusFor(run),
77
+ startedAt: run.startedAt,
78
+ finishedAt: run.finishedAt,
79
+ }))
80
+ .sort((a, b) => b.startedAt - a.startedAt)
81
+
82
+ return {
83
+ chatId: input.chatId,
84
+ armed: input.armed,
85
+ rows,
86
+ rateLimit: input.rateLimit,
87
+ }
88
+ }
@@ -1889,6 +1889,14 @@ export interface SubagentRunSnapshot {
1889
1889
  chatId: string
1890
1890
  subagentId: string | null
1891
1891
  subagentName: string
1892
+ /**
1893
+ * Short human label for this run, derived from the first line of the spawn
1894
+ * prompt (e.g. "Migrate useKannaState.ts"). Null for runs started before this
1895
+ * field existed or by error paths that never carried a prompt. Drives the
1896
+ * Loop Progress panel rows so each round reads as the chunk it worked on
1897
+ * rather than an opaque run id.
1898
+ */
1899
+ label: string | null
1892
1900
  provider: AgentProvider
1893
1901
  model: string
1894
1902
  status: SubagentRunStatus
@@ -1917,6 +1925,41 @@ export interface SubagentRunSnapshot {
1917
1925
  pendingTool: SubagentPendingTool | null
1918
1926
  }
1919
1927
 
1928
+ export type LoopRowStatus = "pending" | "running" | "done" | "failed"
1929
+
1930
+ export interface LoopRow {
1931
+ runId: string
1932
+ label: string
1933
+ status: LoopRowStatus
1934
+ startedAt: number
1935
+ finishedAt: number | null
1936
+ }
1937
+
1938
+ export interface LoopRateLimitInfo {
1939
+ /** The live auto-continue schedule id, so the panel's Resume action can accept it. */
1940
+ scheduleId: string
1941
+ /** epoch ms the usage limit resets (and the resume is/would be scheduled for) */
1942
+ resetAt: number
1943
+ tz: string
1944
+ /**
1945
+ * true → an auto-continue is already scheduled to fire at `resetAt`
1946
+ * (the loop resumes on its own).
1947
+ * false → the resume is only proposed and waits on the user
1948
+ * (auto-resume setting off) — render a "Resume" action.
1949
+ */
1950
+ scheduled: boolean
1951
+ }
1952
+
1953
+ export interface LoopProgressSnapshot {
1954
+ chatId: string
1955
+ /** Whether a loop is currently armed for this chat. */
1956
+ armed: boolean
1957
+ /** Latest first — most recent delegation at the top, mirroring PROGRESS.md. */
1958
+ rows: LoopRow[]
1959
+ /** Non-null while the loop is paused on a Claude usage limit. */
1960
+ rateLimit: LoopRateLimitInfo | null
1961
+ }
1962
+
1920
1963
  export interface ChatSnapshot {
1921
1964
  runtime: ChatRuntime
1922
1965
  queuedMessages: QueuedChatMessage[]
@@ -1931,6 +1974,8 @@ export interface ChatSnapshot {
1931
1974
  liveTunnelId: string | null
1932
1975
  resolvedBindings?: ResolvedStackBinding[]
1933
1976
  subagentRuns: Record<string, SubagentRunSnapshot>
1977
+ /** Loop Progress panel view — armed state, per-round rows, rate-limit resume. */
1978
+ loopProgress: LoopProgressSnapshot
1934
1979
  }
1935
1980
 
1936
1981
  export interface ChatHistoryPage {
@@ -1 +0,0 @@
1
- import{U as a,D as n}from"./mermaid.core-KYiCPJDj.js";const t=(r,o)=>a.lang.round(n.parse(r)[o]);export{t as c};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-727SXJPM-1FNK9_nB.js";import{_ as i}from"./mermaid.core-KYiCPJDj.js";import"./chunk-FMBD7UC4-B7MLrDa7.js";import"./chunk-ND2GUHAM-D4Df05QE.js";import"./chunk-55IACEB6-9-aj4jKO.js";import"./chunk-2J33WTMH-psdZuajw.js";import"./index-zQLLeAfl.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-727SXJPM-1FNK9_nB.js";import{_ as i}from"./mermaid.core-KYiCPJDj.js";import"./chunk-FMBD7UC4-B7MLrDa7.js";import"./chunk-ND2GUHAM-D4Df05QE.js";import"./chunk-55IACEB6-9-aj4jKO.js";import"./chunk-2J33WTMH-psdZuajw.js";import"./index-zQLLeAfl.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};