dsh-side-chat-plus 0.3.2 → 0.3.4

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/src/index.ts CHANGED
@@ -181,12 +181,24 @@ function openTurnStart(events: readonly SideSessionEvent[]): number | undefined
181
181
  return lastStart
182
182
  }
183
183
 
184
+ /**
185
+ * Read a session's event log across DSH versions: 0.1.5+ exposes
186
+ * `snapshotEvents()`; 0.1.2-era sessions carried a plain `events` getter.
187
+ */
188
+ function sessionEvents(session: SideSession): readonly SideSessionEvent[] {
189
+ if (typeof session.snapshotEvents === 'function') {
190
+ return session.snapshotEvents()
191
+ }
192
+ return session.events ?? []
193
+ }
194
+
184
195
  /** Schemastery schema for the user-facing preferences (validated by the settings service). */
185
196
  const PrefsSchema: z<SubchatPrefs> = z.object({
186
197
  lookupDefault: z.boolean().default(SUBCHAT_PREFS_DEFAULTS.lookupDefault),
187
198
  sendImmediately: z.boolean().default(SUBCHAT_PREFS_DEFAULTS.sendImmediately),
188
199
  defaultPrompt: z.string().default(SUBCHAT_PREFS_DEFAULTS.defaultPrompt),
189
200
  bringMode: z.union(['draft', 'context']).default(SUBCHAT_PREFS_DEFAULTS.bringMode),
201
+ panelHome: z.union(['floating', 'sidebar-right']).default(SUBCHAT_PREFS_DEFAULTS.panelHome),
190
202
  })
191
203
 
192
204
  /** Live settings face (bound when the settings service is mounted). */
@@ -311,7 +323,7 @@ function buildApi(ctx: Context, sideChats: Map<string, SidechatRecord>, getSetti
311
323
  const childId = requireString(payload, 'childId')
312
324
  const line = requireString(payload, 'line')
313
325
  const child = childOf(childId)
314
- const result = await ctx.commands.execute(child, line, new AbortController().signal)
326
+ const result = await ctx.commands.execute(child, line, [], new AbortController().signal)
315
327
  return { executed: result !== undefined }
316
328
  }
317
329
 
@@ -319,7 +331,7 @@ function buildApi(ctx: Context, sideChats: Map<string, SidechatRecord>, getSetti
319
331
  const state = (payload: unknown): { plan: { active: boolean; pending: boolean }; goal: { id: string; objective: string } | null } => {
320
332
  const childId = requireString(payload, 'childId')
321
333
  const child = childOf(childId)
322
- const events = child.session.events ?? []
334
+ const events = sessionEvents(child.session)
323
335
  // Plan fold mirrors dsh-plan-mode's `plan` projection.
324
336
  let planActive = false
325
337
  let planWanted: boolean | null = null
@@ -431,7 +443,7 @@ function buildApi(ctx: Context, sideChats: Map<string, SidechatRecord>, getSetti
431
443
  // Client-supplied preset wins; otherwise inherit the launching
432
444
  // conversation's permission preset (skip custom).
433
445
  const explicitPreset = typeof record.preset === 'string' && record.preset !== '' ? record.preset : undefined
434
- const parentPreset = ctx.permissionPresets.current(parent.session.events ?? [])
446
+ const parentPreset = ctx.permissionPresets.current(parent.session)
435
447
  const preset = explicitPreset ?? parentPreset
436
448
  if (preset !== 'custom') {
437
449
  ctx.permissionPresets.set(handle.agent.session, preset)
@@ -472,14 +484,22 @@ function buildApi(ctx: Context, sideChats: Map<string, SidechatRecord>, getSetti
472
484
  const items: Array<{ childId: string; running: boolean; runningSince?: number }> = []
473
485
  for (const record of sideChats.values()) {
474
486
  if (record.parentSessionId !== parentSessionId) continue
475
- // "Running" is decided by an open turn in the session log — the same
476
- // signal the main conversation usesinstead of the agent status getter,
477
- // so a turn that already settled can never leave the panel stuck on
478
- // "thinking".
479
- const runningSince = openTurnStart(record.handle.agent.session.events ?? [])
487
+ const agent = record.handle.agent
488
+ // "Running" follows the agent's live lifecycle status — the dominant
489
+ // signal the main conversation uses so the composer flips to the stop
490
+ // button the moment the driver wakes for thinking/streaming, instead of
491
+ // waiting for a durable turn/start to land in the log. A settled driver
492
+ // is always 'idle' again, so a finished turn can never leave the panel
493
+ // stuck on "thinking". Hosts that do not surface the status getter fall
494
+ // back to the open-turn event scan.
495
+ const status = (agent as { status?: string }).status
496
+ const statusRunning = status === 'running'
497
+ const eventRunningSince = openTurnStart(sessionEvents(agent.session))
498
+ const running = statusRunning || (status === undefined && eventRunningSince !== undefined)
499
+ const runningSince = running ? (eventRunningSince ?? Date.now()) : undefined
480
500
  items.push({
481
501
  childId: record.childId,
482
- running: runningSince !== undefined,
502
+ running,
483
503
  ...(runningSince !== undefined ? { runningSince } : {}),
484
504
  })
485
505
  }
@@ -12,6 +12,9 @@ export const SUBCHAT_PREFS_NS = 'dsh-side-chat'
12
12
  /** How a brought-back reply lands in the main conversation. */
13
13
  export type BringMode = 'draft' | 'context'
14
14
 
15
+ /** Where the side-chat panel lives when a side chat is open. */
16
+ export type PanelHome = 'floating' | 'sidebar-right'
17
+
15
18
  /** User-facing side-chat preferences. */
16
19
  export interface SubchatPrefs {
17
20
  /** Whether the "look up workspace / parent when needed" switch defaults on. */
@@ -22,6 +25,8 @@ export interface SubchatPrefs {
22
25
  defaultPrompt: string
23
26
  /** How brought-back content lands: into the composer draft, or as a collapsed context row. */
24
27
  bringMode: BringMode
28
+ /** Which container hosts the panel: the classic floating right-edge panel, or the new built-in right sidebar. */
29
+ panelHome: PanelHome
25
30
  }
26
31
 
27
32
  /** Fallback prefs used whenever the settings document is unreachable or malformed. */
@@ -30,4 +35,5 @@ export const SUBCHAT_PREFS_DEFAULTS: SubchatPrefs = {
30
35
  sendImmediately: true,
31
36
  defaultPrompt: '',
32
37
  bringMode: 'draft',
38
+ panelHome: 'sidebar-right',
33
39
  }