feat(irc): add interactive setup

feat(gateway): refine Platform._missing_ and platform-connected dispatch

Restricts plugin-name acceptance to bundled plugin scan + registry
(no arbitrary string -> enum-pollution), pulls per-platform connectivity
checks into a _PLATFORM_CONNECTED_CHECKERS lambda map with a clean
_is_platform_connected method, and adds tests covering the checker map,
plugin platform interface, and IRC setup wizard.
This commit is contained in:
Ari Lotter
2026-04-20 18:52:15 -04:00
committed by Teknium
parent 6e42daf7dd
commit 868bc1c242
38 changed files with 2191 additions and 189 deletions

View File

@@ -41,10 +41,10 @@ export interface SelectionApi {
captureScrolledRows: (firstRow: number, lastRow: number, side: 'above' | 'below') => void
clearSelection: () => void
copySelection: () => Promise<string>
copySelectionNoClear: () => Promise<string>
getState: () => unknown
version: () => number
shiftAnchor: (dRow: number, minRow: number, maxRow: number) => void
copySelectionNoClear: () => Promise<string>;
getState: () => unknown;
version: () => number;
shiftAnchor: (dRow: number, minRow: number, maxRow: number) => void;
shiftSelection: (dRow: number, minRow: number, maxRow: number) => void
}

View File

@@ -437,6 +437,39 @@ export const coreCommands: SlashCommand[] = [
}
},
{
help: 'save the current transcript to JSON',
name: 'save',
run: (_arg, ctx) => {
const hasConversation = ctx.local
.getHistoryItems()
.some(m => m.role === 'user' || m.role === 'assistant' || m.role === 'tool')
if (!hasConversation) {
return ctx.transcript.sys('no conversation yet')
}
if (!ctx.sid) {
return ctx.transcript.sys('no active session — nothing to save')
}
ctx.gateway
.rpc<SessionSaveResponse>('session.save', { session_id: ctx.sid })
.then(
ctx.guarded<SessionSaveResponse>(r => {
const file = r?.file
if (file) {
ctx.transcript.sys(`conversation saved to: ${file}`)
} else {
ctx.transcript.sys('failed to save')
}
})
)
.catch(ctx.guardedErr)
}
},
{
aliases: ['sb'],
help: 'status bar position (on|off|top|bottom)',

View File

@@ -904,6 +904,21 @@ export const ToolTrail = memo(function ToolTrail({
)
}
const toolLabel = (group: Group) => {
const { duration, label } = splitToolDuration(String(group.content))
return duration ? (
<>
{label}
<Text color={t.color.statusFg} dim>
{duration}
</Text>
</>
) : (
group.content
)
}
// ── Backstop: floating alerts when every panel is hidden ─────────
//
// Per-section overrides win over the global details_mode (they're computed

View File

@@ -1,3 +1,5 @@
import { evictInkCaches } from '@hermes/ink'
import { type HeapDumpResult, performHeapDump } from './memory.js'
export type MemoryLevel = 'critical' | 'high' | 'normal'
@@ -71,6 +73,10 @@ export function startMemoryMonitor({
return
}
// Prune Ink content caches before dump/exit — half on 'high' (recoverable),
// full on 'critical' (post-dump RSS reduction, keeps user running).
evictInkCaches(level === 'critical' ? 'all' : 'half')
if (dumped.has(level) || inFlight.has(level)) {
return
}

View File

@@ -142,12 +142,12 @@ export interface McpServerStatus {
export interface SessionInfo {
cwd?: string
fast?: boolean
lazy?: boolean
mcp_servers?: McpServerStatus[]
model: string
reasoning_effort?: string
release_date?: string
fast?: boolean;
lazy?: boolean;
mcp_servers?: McpServerStatus[];
model: string;
reasoning_effort?: string;
release_date?: string;
service_tier?: string
skills: Record<string, string[]>
tools: Record<string, string[]>

View File

@@ -59,6 +59,32 @@ declare module '@hermes/ink' {
}>
}
export type FrameEvent = {
readonly durationMs: number
readonly phases?: {
readonly renderer: number
readonly diff: number
readonly optimize: number
readonly write: number
readonly patches: number
readonly optimizedPatches: number
readonly writeBytes: number
readonly backpressure: boolean
readonly prevFrameDrainMs: number
readonly yoga: number
readonly commit: number
readonly yogaVisited: number
readonly yogaMeasured: number
readonly yogaCacheHits: number
readonly yogaLive: number
}
readonly flickers: ReadonlyArray<{
readonly desiredHeight: number
readonly availableHeight: number
readonly reason: 'resize' | 'offscreen' | 'clear'
}>
}
export type RenderOptions = {
readonly stdin?: NodeJS.ReadStream
readonly stdout?: NodeJS.WriteStream