dsh-side-chat-plus 0.3.4 → 0.4.0-alpha.1

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
@@ -23,6 +23,7 @@ import type {
23
23
  SideImageAttachmentRef,
24
24
  SideInstallModelSelection,
25
25
  SideModelSelectionRef,
26
+ SidePresetOption,
26
27
  SideSession,
27
28
  SideSessionEvent,
28
29
  } from './context-types.ts'
@@ -40,7 +41,6 @@ export const inject = [
40
41
  'agents',
41
42
  'workspaceRegistry',
42
43
  'sessionQuery',
43
- 'sandboxPolicy',
44
44
  'permissionPresets',
45
45
  'agentPresets',
46
46
  'llm',
@@ -181,17 +181,6 @@ 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
-
195
184
  /** Schemastery schema for the user-facing preferences (validated by the settings service). */
196
185
  const PrefsSchema: z<SubchatPrefs> = z.object({
197
186
  lookupDefault: z.boolean().default(SUBCHAT_PREFS_DEFAULTS.lookupDefault),
@@ -327,11 +316,20 @@ function buildApi(ctx: Context, sideChats: Map<string, SidechatRecord>, getSetti
327
316
  return { executed: result !== undefined }
328
317
  }
329
318
 
319
+ /**
320
+ * Read a side chat's event log through the session-query service. DSH 0.1.6
321
+ * deprecated the Session's synchronous event getters (`snapshotEvents`,
322
+ * `eventAt`, `ownEvents`); `readSession` is the live-preferred asynchronous
323
+ * read that supersedes them, and the transcript routes already use it.
324
+ */
325
+ const sessionEvents = async (sessionId: string): Promise<readonly SideSessionEvent[]> =>
326
+ (await ctx.sessionQuery.readSession(sessionId)).events
327
+
330
328
  /** Fold one side-chat agent's plan/goal state for its composer chrome. */
331
- const state = (payload: unknown): { plan: { active: boolean; pending: boolean }; goal: { id: string; objective: string } | null } => {
329
+ const state = async (payload: unknown): Promise<{ plan: { active: boolean; pending: boolean }; goal: { id: string; objective: string } | null }> => {
332
330
  const childId = requireString(payload, 'childId')
333
- const child = childOf(childId)
334
- const events = sessionEvents(child.session)
331
+ childOf(childId)
332
+ const events = await sessionEvents(childId)
335
333
  // Plan fold mirrors dsh-plan-mode's `plan` projection.
336
334
  let planActive = false
337
335
  let planWanted: boolean | null = null
@@ -479,7 +477,7 @@ function buildApi(ctx: Context, sideChats: Map<string, SidechatRecord>, getSetti
479
477
  }
480
478
 
481
479
  /** List the side chats launched by one parent conversation. */
482
- const list = (payload: unknown): { items: Array<{ childId: string; running: boolean; runningSince?: number }> } => {
480
+ const list = async (payload: unknown): Promise<{ items: Array<{ childId: string; running: boolean; runningSince?: number }> }> => {
483
481
  const parentSessionId = requireString(payload, 'parentSessionId')
484
482
  const items: Array<{ childId: string; running: boolean; runningSince?: number }> = []
485
483
  for (const record of sideChats.values()) {
@@ -494,7 +492,8 @@ function buildApi(ctx: Context, sideChats: Map<string, SidechatRecord>, getSetti
494
492
  // back to the open-turn event scan.
495
493
  const status = (agent as { status?: string }).status
496
494
  const statusRunning = status === 'running'
497
- const eventRunningSince = openTurnStart(sessionEvents(agent.session))
495
+ // Only the status-less fallback pays for a log read.
496
+ const eventRunningSince = status === undefined ? openTurnStart(await sessionEvents(record.childId)) : undefined
498
497
  const running = statusRunning || (status === undefined && eventRunningSince !== undefined)
499
498
  const runningSince = running ? (eventRunningSince ?? Date.now()) : undefined
500
499
  items.push({
@@ -522,7 +521,7 @@ function buildApi(ctx: Context, sideChats: Map<string, SidechatRecord>, getSetti
522
521
  }
523
522
 
524
523
  /** The deployment-resolved image policy (for client-side fast-path checks). */
525
- const limits = (): { mediaTypes: string[]; maxImageBytes: number; maxImagesPerMessage: number; maxMessageImageBytes: number; maxImagePixels: number } => {
524
+ const limits = (): { mediaTypes: string[]; maxImageBytes: number; maxImagesPerMessage: number; maxMessageImageBytes: number; maxImagePixels: number; maxImageDimension?: number } => {
526
525
  const l = ctx.attachments.imageLimits
527
526
  return {
528
527
  mediaTypes: [...l.mediaTypes],
@@ -530,6 +529,8 @@ function buildApi(ctx: Context, sideChats: Map<string, SidechatRecord>, getSetti
530
529
  maxImagesPerMessage: l.maxImagesPerMessage,
531
530
  maxMessageImageBytes: l.maxMessageImageBytes,
532
531
  maxImagePixels: l.maxImagePixels,
532
+ // Added in 0.1.6; earlier lines leave it absent so the client skips the check.
533
+ ...(l.maxImageDimension === undefined ? {} : { maxImageDimension: l.maxImageDimension }),
533
534
  }
534
535
  }
535
536
 
@@ -718,11 +719,31 @@ function buildApi(ctx: Context, sideChats: Map<string, SidechatRecord>, getSetti
718
719
 
719
720
  /** Permission-preset options for the side-chat selector. */
720
721
  const permissions = (): { options: Array<{ value: string; name: string; description?: string }>; current: string } => {
721
- const select = ctx.permissionPresets.selectFor({})
722
- return {
723
- options: select.options.map((o) => ({ value: o.value, name: o.name, ...(o.description === undefined ? {} : { description: o.description }) })),
724
- current: select.currentValue,
722
+ const presets = ctx.permissionPresets
723
+ const toOption = (o: SidePresetOption): { value: string; name: string; description?: string } => ({
724
+ value: o.value,
725
+ name: o.name,
726
+ ...(o.description === undefined ? {} : { description: o.description }),
727
+ })
728
+ // 0.1.6 split the combined `selectFor(knobState)` into a process-level
729
+ // `catalog()` plus a separate current value. Prefer the new face and fall
730
+ // back to the old one so both release lines serve this route.
731
+ const catalog = presets.catalog
732
+ if (typeof catalog === 'function') {
733
+ return {
734
+ options: catalog.call(presets).options.map(toOption),
735
+ current: presets.defaultPreset ?? 'custom',
736
+ }
737
+ }
738
+ const selectFor = presets.selectFor
739
+ if (typeof selectFor === 'function') {
740
+ const select = selectFor.call(presets, {})
741
+ return {
742
+ options: select.options.map(toOption),
743
+ current: select.currentValue,
744
+ }
725
745
  }
746
+ throw new SidechatError('permissions-unavailable', 'the permission-presets service exposes neither catalog() nor selectFor()', 503)
726
747
  }
727
748
 
728
749
  return {
@@ -826,6 +847,23 @@ export function apply(ctx: Context): void {
826
847
  }
827
848
  }, 'dsh-side-chat: dispose side chats')
828
849
 
850
+ // DSH 0.1.6 lists archived sessions in Settings and lets the user restore
851
+ // one. A side chat is archived precisely so it stays out of the session
852
+ // list, so at activation re-archive every recorded side chat that a restore
853
+ // (or an older run) left visible again. Best-effort: records whose session
854
+ // no longer exists are simply skipped.
855
+ ctx.effect(() => {
856
+ void (async () => {
857
+ for (const record of await readRecords()) {
858
+ try {
859
+ await ctx.workspaceRegistry.archiveSession(record.childId)
860
+ } catch {
861
+ // Unknown/expired ids are expected as the record list ages out.
862
+ }
863
+ }
864
+ })()
865
+ }, 'dsh-side-chat: re-archive recorded side chats')
866
+
829
867
  ctx.effect(() => ctx.webServer.register({
830
868
  kind: 'prefix',
831
869
  path: '/sidechat/api',