feat: slash system / input service / agent scope
This commit is contained in:
58
packages/client/ui-subagent/src/client/index.ts
Normal file
58
packages/client/ui-subagent/src/client/index.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Subagent reference plugin, browser half: registers the '@' source —
|
||||
* candidates filtered from the session list snapshot's running children
|
||||
* (zero RPC; the list rides the plugin's root-context sessions service, the
|
||||
* scoped session comes from the per-call projection), pick inserts the
|
||||
* literal `@label ` text (decision 21: the draft carries plain text, chip
|
||||
* visuals are derived by scanning against the source lexicon, and the
|
||||
* prompt ships the same literal). Consumption semantics stay with future
|
||||
* business work (design ledger). No adjudication hooks: subagent
|
||||
* references never enter command adjudication.
|
||||
*/
|
||||
import type { ClientContext, SessionsService } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { ClientSessionContext, SlashServiceContract, SlashSource } from '@deepseek-ai/dsh-client-ui-slash/client'
|
||||
|
||||
/** Required services: the slash registry + the session list face the source closes over. */
|
||||
export const inject = ['slash', 'sessions']
|
||||
|
||||
/**
|
||||
* Client plugin body: register the '@' subagent source over the root session list.
|
||||
* @param ctx - client root context.
|
||||
*/
|
||||
export function apply(ctx: ClientContext): void {
|
||||
const sessions = ctx.get('sessions') as SessionsService
|
||||
// Child labels live on the session list (parentId lineage + displayTitle),
|
||||
// not the conversation snapshot — the list store is the zero-RPC candidate feed.
|
||||
const childLabels = (session: ClientSessionContext, query: string): string[] => {
|
||||
const { byId } = sessions.list.getSnapshot()
|
||||
return Object.values(byId)
|
||||
.filter((child) => child.parentId === session.sessionId && child.running && child.displayTitle.includes(query))
|
||||
.map((child) => child.displayTitle)
|
||||
}
|
||||
const source: SlashSource = {
|
||||
trigger: '@',
|
||||
name: 'subagent',
|
||||
candidates(session, { query }) {
|
||||
return Promise.resolve(childLabels(session, query).map((name) => ({ name })))
|
||||
},
|
||||
lexicon(session) {
|
||||
// The list snapshot is always warm — the full running-children roster.
|
||||
return childLabels(session, '')
|
||||
},
|
||||
onPick({ candidate }) {
|
||||
// Decision 21: plain-text reference — the literal lands in the draft
|
||||
// and ships to the model verbatim (trailing space closes the token).
|
||||
// Legacy path (decision 21), retained for the removal cut, no longer reached:
|
||||
// return { insert: { source: 'subagent', ref: candidate.name, label: candidate.name, clipboardText: `@${candidate.name}` } }
|
||||
return { text: `@${candidate.name} ` }
|
||||
},
|
||||
codec: {
|
||||
clipboardText: (ref) => `@${ref}`,
|
||||
// TODO: serialize returns the raw label until the '@' consumption
|
||||
// feature defines a model representation (design ledger).
|
||||
serialize: (ref) => Promise.resolve(`@${ref}`),
|
||||
},
|
||||
}
|
||||
const slash = ctx.get('slash') as SlashServiceContract
|
||||
ctx.effect(() => slash.registerSource(source), 'ui-subagent: @ source')
|
||||
}
|
||||
6
packages/client/ui-subagent/src/css-modules.d.ts
vendored
Normal file
6
packages/client/ui-subagent/src/css-modules.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
declare module '*.module.css' {
|
||||
const classes: Record<string, string>
|
||||
export default classes
|
||||
}
|
||||
|
||||
declare module '*.css'
|
||||
9
packages/client/ui-subagent/src/index.ts
Normal file
9
packages/client/ui-subagent/src/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Subagent reference plugin, node half. Pure UI plugin: the empty apply
|
||||
* exists so the plugin appears in the host cordis.yml / Loader; the browser
|
||||
* half ships via exports["./client"], discovered through the package.json
|
||||
* dshClient declaration.
|
||||
*/
|
||||
|
||||
/** Host plugin body — no host-side behavior for this source plugin. */
|
||||
export function apply(): void {}
|
||||
31
packages/client/ui-subagent/src/invariant.ts
Normal file
31
packages/client/ui-subagent/src/invariant.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Package-owned invariant companion for `@deepseek-ai/dsh-client-ui-subagent`.
|
||||
* @module @deepseek-ai/dsh-client-ui-subagent/invariant
|
||||
*/
|
||||
|
||||
/* jscpd:ignore-start */
|
||||
import type { Context } from 'cordis'
|
||||
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
|
||||
|
||||
const PACKAGE_NAME = '@deepseek-ai/dsh-client-ui-subagent'
|
||||
|
||||
/** Cordis companion plugin name. */
|
||||
export const name = 'client-ui-subagent-invariant'
|
||||
/** Service required before the companion can reserve package ownership. */
|
||||
export const inject = ['invariants']
|
||||
|
||||
/**
|
||||
* No runtime invariant: a single slash-source registration whose disposal is
|
||||
* proven by the HMR-safety spec — it emits no cordis events and owns no
|
||||
* cross-plugin mutable state.
|
||||
*/
|
||||
const install: InvariantInstaller = () => {}
|
||||
|
||||
/**
|
||||
* Register this package's invariant companion.
|
||||
* @param ctx - Cordis context carrying the invariant service.
|
||||
* @returns the installed registration's disposer after setup succeeds.
|
||||
*/
|
||||
export const apply = (ctx: Context): Promise<() => void> =>
|
||||
Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install))
|
||||
/* jscpd:ignore-end */
|
||||
Reference in New Issue
Block a user