@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.
- package/dist/client/assets/{arc-BLk1CZKB.js → arc-CAysfmX5.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-B01ZQYte.js → architectureDiagram-3BPJPVTR-BKqRYsm4.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-diL6a3cq.js → blockDiagram-GPEHLZMM-Ce7Eorec.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-BY1verL8.js → c4Diagram-AAUBKEIU-CJ77zauH.js} +1 -1
- package/dist/client/assets/channel-KQhRwDzJ.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-psdZuajw.js → chunk-2J33WTMH-Rk_3zLxJ.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-CrwQVBc5.js → chunk-4BX2VUAB-DPIz6Nvf.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-9-aj4jKO.js → chunk-55IACEB6-D-TkbGh2.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-1FNK9_nB.js → chunk-727SXJPM-B4NOSIZV.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-DFhvd_Ek.js → chunk-AQP2D5EJ-Cm_TGLYm.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-B7MLrDa7.js → chunk-FMBD7UC4-Bu45BUVh.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-D4Df05QE.js → chunk-ND2GUHAM-C1Bw8_gg.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-Iq17Dznm.js → chunk-QZHKN3VN-DueG15o0.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-ChxT1GNq.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-ChxT1GNq.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-BmaZhtXy.js → cose-bilkent-S5V4N54A-YgpMurXf.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-BCmpfnfP.js → dagre-BM42HDAG-Dop7Oq67.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-BIEcKowS.js → diagram-2AECGRRQ-unMfBEJH.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-CRBABBfv.js → diagram-5GNKFQAL-D9EJ9qMI.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-BRxUk16N.js → diagram-KO2AKTUF-C5IykOIR.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-DPBs4xl1.js → diagram-LMA3HP47-DJAuPngz.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-BE9rgVv8.js → diagram-OG6HWLK6-B3wHFtS-.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-D3vgA1xJ.js → erDiagram-TEJ5UH35-Dm7kt9o5.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-Cgu-8XXC.js → flowDiagram-I6XJVG4X-s9Lds3ny.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-CgjXVyTR.js → ganttDiagram-6RSMTGT7-CRQNYdAf.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-zD5D8B-i.js → gitGraphDiagram-PVQCEYII-DhYjK4Jb.js} +1 -1
- package/dist/client/assets/{index-zQLLeAfl.js → index-C0U2kwuB.js} +253 -243
- package/dist/client/assets/index-CLsOWXff.css +32 -0
- package/dist/client/assets/{index-DBTdhMbJ.js → index-CbSY9HfS.js} +1 -1
- package/dist/client/assets/{infoDiagram-5YYISTIA-BaFHqT4u.js → infoDiagram-5YYISTIA-CRKJ0PSH.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-2yAC1br2.js → ishikawaDiagram-YF4QCWOH-CEAkB0IY.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-DacQ-ND5.js → journeyDiagram-JHISSGLW-CyOK5_DG.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-Qlbwm3z4.js → kanban-definition-UN3LZRKU-BfKjiJSo.js} +1 -1
- package/dist/client/assets/{linear-DeafqpgO.js → linear-DLR6miy3.js} +1 -1
- package/dist/client/assets/{mermaid.core-KYiCPJDj.js → mermaid.core-D7AdC_lE.js} +4 -4
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-B1V-T6d3.js → mindmap-definition-RKZ34NQL-zYyJtDSa.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-DOF9OFRu.js → pieDiagram-4H26LBE5-BgP8PFlo.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-lH1mB7ko.js → quadrantDiagram-W4KKPZXB-Di8h7J96.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-BaTsJ_sS.js → requirementDiagram-4Y6WPE33-DuTzFl3R.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-Pfxib5iI.js → sankeyDiagram-5OEKKPKP-DBRoMm_T.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BQMEKkBC.js → sequenceDiagram-3UESZ5HK-Bmb-1HUN.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-CuldNnzp.js → stateDiagram-AJRCARHV-fnv5ayn-.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-D4X-ZnLV.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-DP7u0YAg.js → timeline-definition-PNZ67QCA-ByWRdpQX.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-QlrjpNeQ.js → vennDiagram-CIIHVFJN--2WGpVXW.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-BqdJ_sAM.js → wardley-L42UT6IY-24zlnzxK.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-Wi3kaQ5X.js → wardleyDiagram-YWT4CUSO-Cllvqb3K.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-D-KXMyCG.js → xychartDiagram-2RQKCTM6-ZcdC6nPE.js} +1 -1
- package/dist/client/index.html +2 -2
- package/package.json +1 -1
- package/src/server/agent.oauth-rotation.test.ts +101 -0
- package/src/server/agent.test.ts +3 -3
- package/src/server/agent.ts +24 -0
- package/src/server/auto-continue/read-model.test.ts +2 -2
- package/src/server/auto-continue/read-model.ts +4 -1
- package/src/server/event-store.ts +1 -0
- package/src/server/events.ts +3 -0
- package/src/server/read-models.ts +26 -1
- package/src/server/subagent-orchestrator.ts +3 -0
- package/src/shared/loop-progress.test.ts +106 -0
- package/src/shared/loop-progress.ts +88 -0
- package/src/shared/types.ts +45 -0
- 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
|
@@ -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
|
}
|
package/src/server/events.ts
CHANGED
|
@@ -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
|
+
}
|
package/src/shared/types.ts
CHANGED
|
@@ -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};
|