Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/feature-map/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ this area is reached from inside it unless stated otherwise.
| Session summary | Right-panel rolling summary of the conversation. With **Pin Summary in Side Panel** on (and session summaries on), Summary is the first pinned tab, cannot be closed, and is the tab the panel opens on; a panel opened for a file or another item shows that item | Chat right panel → **Summary** tab (**+** menu); Settings → Chat → Side panel → **Pin Summary in Side Panel** | `pages/chat/SessionSummaryTab.tsx`, `pages/chat/SidePanelAddMenu.tsx` (the **+** menu's Summary row, hidden while summaries are off or Summary is pinned), `pages/chat/usePinnedViews.ts` (the pin and the open-on-Summary rule), `hooks/usePanelTabs.ts` (`takePlainPanelShow`, the per-chat count of tab requests), `pages/chat/page/paneLayout.tsx` (ends a show when the panel closes or its chat changes), `pages/chat/SidePanel.tsx`, `pages/settings/ChatPanel.tsx` (the toggle) | `chat_handlers.py`, `file_api/dashboard_config.py` (the setting) | `GET,POST /api/chat/slots/{slot}/summary`, `GET,PUT /api/dashboard/config` (`side_panel_pin_summary`) |
| Keep side panel open | Opens the chat's right side panel in every chat: a new chat, a chat you switch to, and the chat a reload lands on. Closing it hides it until you switch chats. Each chat's own saved open or closed choice is kept, and turning the setting off brings it back. Off on a phone and in an embedded companion chat; a popped-out chat window keeps it | Settings → Chat → Side panel → **Keep Side Panel Open** | `pages/settings/ChatPanel.tsx` (the toggle), `pages/chat/page/paneLayout.tsx` (applies it), `store/chat/activity.ts` (`setSidePanelAlwaysOpen`, `loadSlotActivity`) | `file_api/dashboard_config.py` | `GET,PUT /api/dashboard/config` (`side_panel_always_open`) |
| Session cleanup | Bulk-archive inactive sessions to Older Sessions in one step: pick a window (no activity in the last 1, 3 or 7 days) and a dry-run preview lists the sessions that would move, with a count and an expandable list. The active session and pinned sessions are kept (the active one is reported as skipped when it is itself stale), and a slot that owns an armed auto-nudge loop is exempt because archiving it would mark it closed and terminally remove the loop; when the auto-nudge registry cannot be read the pass archives nothing and fails closed | Sidebar header ⋮ menu → **Clean up sessions** | `pages/chat-sidebar/sessionCleanup.tsx` (the cleanup panel and its day-window preview) | `chat_api/slot_lifecycle.py` (`api_chat_slots_cleanup`) | `POST /api/chat/slots/cleanup` |
| Regenerate all titles | Give every open session a fresh title from its recent conversation in one step. A dry-run preview counts the sessions it would retitle and the ones it leaves alone: running sessions, sessions with no conversation, sessions whose title limit of one bulk or agent call per 5 minutes is spent and sessions you renamed by hand, which a checkbox can include. Up to two titles generate at once, each lands in the sidebar as it finishes, and the panel counts the run down and reports how many were retitled, left as they were or failed, naming each failed session as a link that opens it | Sidebar header ⋮ menu → **Regenerate all titles…** | `pages/chat-sidebar/bulkTitles.tsx` | `chat_title_bulk.py` (`api_chat_slots_generate_titles`) | `POST /api/chat/generate-titles` (`dry_run`, `include_renamed`), `GET /api/chat/generate-titles` (progress) |
| Broadcast message | Send one message to many open sessions at once. Quick picks (in a loop, in progress, needs me, all) show live counts, and the panel opens on the first one that reaches anyone; mate chats are only, included or excluded (excluded by default); folded custom criteria match any chosen state and narrow by folder with its sub-folders, tag, pinned or activity in the last 24 hours. Each matched session can be unticked. A session's own running turn gets the message as a steer, taken in after its current tool call (or after the user answers an approval or question it waits on); any other session gets a plain send, so the hold behind live sub-agents and queued messages applies, and an idle one starts a new turn. Sessions that cannot take a message (a peer crew's, a read-only relay archive) are never matched. Only an explicitly refused session can be retried; one whose receipt is unknown is never resent, and its row opens that session | Sidebar header ⋮ menu → **Broadcast message…** | `pages/chat-sidebar/BroadcastPanel.tsx` (the panel), `lib/broadcastTargets.ts` (who a broadcast reaches) | `chat_api/chat_send.py` (`api_chat`, steer or plain send) | `POST /api/chat` |
| Crew log | Right-panel read of the session's append-only record, as the six folds the backend serves: lifecycle and turn status, credits / tokens / compactions, a timeline window, per-tool totals with the calls still open, approvals with what is waiting on the user, and the subagents this session dispatched with each one's outcome, duration and credits. Each fold is a section whose collapsed header carries its own figures; the footer states the record seq the values were folded through. A slot-key read resolves to the session the slot's turns run on, so a channel-linked slot folds the channel session's record rather than an empty one. Re-reads when the session's own turn stops and when its spawned work goes idle, plus a manual control; a read waits briefly for the writer to drain and says so when it could not, and an id with no addressable unit is reported as that rather than as nothing recorded. Diagnostics, listed with Logs and Context | Chat right panel → **+** menu → **Crew log**; also under `/members`, where the Crew Members side panel is the same `SidePanel` and offers the row once the member's thread confirms the slot | `pages/chat/CrewLogPanel.tsx`, `pages/chat/ActivityViewer.tsx`, `pages/chat/SidePanel.tsx` | `handlers/crew_log.py` | `GET /api/sessions/{id}/crew-log/projections` (every advertised panel fold from one read), `GET /api/sessions/{id}/crew-log/projection/{status,usage,timeline,tools,approvals,subagents}` |
| Crew panel | An Ultra Mode chat's crew (code name Crew Mode) in its right side panel, as a **Crew** tab pinned first, opened on entry, with two views. **Status** (the default) shows the Overview (the ledger goal in one sentence and one "Done when" sentence), **Needs your input** (the crew's tool approvals and the main chat's question, as compact cards answered in place), **Milestones** (one row per work-ledger item: done, waiting on you, running or next, with a moving or stalled line) and a one-line **Recap** (the session summary's line when it is about goal progress, else a milestone sentence). **List** shows the crew as a tree, sub-teams indented under their lead and finished members folded away; a click on a member opens it in the main pane. The root's sidebar row carries a small ghost and "N sessions · M running". Offered only for a chat carrying a crew root while the Ultra Mode Feature Preview is on; every other chat's panel is unchanged | Chat right panel of an Ultra Mode chat → **Crew** tab (first, no ✕); Status \| List switch at the top | `pages/chat/command-center/UltraCrewPanel.tsx`, `pages/chat/command-center/CrewWorkerList.tsx`, `pages/chat/SidePanel.tsx`, `pages/chat/usePinnedViews.ts`, `pages/chat/command-center/AttentionCard.tsx`, `pages/chat-sidebar/SessionRow.tsx` | `dashboard/state_parts/slots_push.py` (the `crew_root` / `crew_mode_root` stamps; `crew_mode.py`), the crew list handler in `dashboard/handlers/crew_workers.py` (see **Crew panel routes**), the work-ledger board (`dashboard/handlers/work_ledger_board.py`) and the session summary (`GET /api/chat/slots/{slot}/summary`); a 404 from an older gateway is treated as no data (`crewRouteNotServed`) | `GET /api/chat/slots?crew=all`, `GET /api/chat/slots/{slot}/crew-workers`, `GET /api/crew-board?conductor={slot}`, `GET /api/chat/slots/{slot}/summary` |
Expand Down
4 changes: 2 additions & 2 deletions docs/system-specs/modules/learn-cron-dashboard.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/kiro_crew/dashboard/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@
api_chat_slot_generate_title,
api_chat_slot_rename,
)
from kiro_crew.dashboard.chat_title_bulk import ( # noqa: F401
api_chat_slots_generate_titles,
api_chat_slots_generate_titles_status,
)
from kiro_crew.dashboard.chat_todo import api_chat_slot_todo # noqa: F401
from kiro_crew.dashboard.chat_utils import ( # noqa: F401
_BLOCKED_SLASH_COMMANDS,
Expand Down
40 changes: 22 additions & 18 deletions src/kiro_crew/dashboard/chat_title.py
Original file line number Diff line number Diff line change
Expand Up @@ -1716,6 +1716,27 @@ def superseded() -> bool:
slot._title_in_flight = False


async def _model_manual_title(state: DashboardState, slot: _ChatSlot) -> str:
# Window the RECENT conversational tail: the user reaches for
# "Regenerate title" when the current name doesn't fit, so the
# prompt must be built from what the session is about NOW, not its
# opening turns. Filter to the roles the prompt builder keeps BEFORE
# slicing — the raw list interleaves tool/permission/status rows, so
# a raw tail over a tool-heavy turn could window every usable line
# out and leave the click a silent no-op. The prompt builder's head
# slice is a no-op on this pre-windowed tail, so the initial
# auto-title path (which passes the full list and wants the opening
# messages) is unaffected.
convo = [m for m in slot.messages if m.get("role") in _TITLE_PROMPT_ROLES]
# Same label as the automatic paths: the key the title already leads
# with, else the opening message's.
key = await _ticket_key_if_enabled(_current_ticket_key(slot.title, convo))
title = await _generate_title_via_kiro(
state, convo[-_TITLE_PROMPT_WINDOW:], session_key=effective_session_key(slot)
)
return _with_ticket_prefix(title, key)


async def generate_manual_title(state: DashboardState, slot: _ChatSlot) -> str:
"""The title "Regenerate title" would give *slot* now, or ``""`` for none.

Expand All @@ -1728,24 +1749,7 @@ async def generate_manual_title(state: DashboardState, slot: _ChatSlot) -> str:
if _is_member_thread(slot):
return ""
try:
# Window the RECENT conversational tail: the user reaches for
# "Regenerate title" when the current name no longer fits, so the
# prompt must be built from what the session is about NOW, not its
# opening turns. Filter to the roles the prompt builder keeps BEFORE
# slicing — the raw list interleaves tool/permission/status rows, so
# a raw tail over a tool-heavy turn could window every usable line
# out and leave the click a silent no-op. The prompt builder's head
# slice is a no-op on this pre-windowed tail, so the initial
# auto-title path (which passes the full list and wants the opening
# messages) is unaffected.
convo = [m for m in slot.messages if m.get("role") in _TITLE_PROMPT_ROLES]
# Same label as the automatic paths: the key the title already leads
# with, else the opening message's.
key = await _ticket_key_if_enabled(_current_ticket_key(slot.title, convo))
title = await _generate_title_via_kiro(
state, convo[-_TITLE_PROMPT_WINDOW:], session_key=effective_session_key(slot)
)
return _with_ticket_prefix(title, key)
return await _model_manual_title(state, slot)
except Exception:
logger.debug("Title generation failed for slot %s", slot.key, exc_info=True)
title = _fallback_title_from_messages(slot.messages)
Expand Down
Loading
Loading