fix: test/docs

This commit is contained in:
imccyu
2026-08-13 00:19:15 +08:00
parent ffcc8e299b
commit 978e573605
33 changed files with 1307 additions and 136 deletions

View File

@@ -122,36 +122,36 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
{
signature: 'subscribe(fn: () => void): () => void',
description: 'LocaleFace subscribe: notified on every snapshot change (locale switch or dictionary registration — registrations bump the revision so already rendered outlets pick up late-arriving dictionaries).',
parameters: [{"name":"fn","description":"change callback."}],
parameters: [{ name: 'fn', description: 'change callback.' }],
returns: 'unsubscribe.',
},
{
signature: 'setLocale(id: string): void',
description: 'Switch the active locale — the only user preference write entry.',
parameters: [{"name":"id","description":"a registered locale id; unknown ids throw."}],
parameters: [{ name: 'id', description: 'a registered locale id; unknown ids throw.' }],
},
{
signature: 'register<N extends keyof LocaleNamespaceMap & string>(ns: N, dicts: Record<LocaleId, LocaleDictOf<N>>): () => void',
description: 'Register a declared namespace\'s dictionaries, all locales in one call — the typed form: each dictionary is checked against the namespace\'s LocaleNamespaceMap key union (a missing or extra key is a compile error), and every shipped locale is required (bilingual balance enforced at registration). Duplicate (ns, locale) throws (single occupant; a namespace\'s texts have one owner). Registration bumps the revision so mounted outlets pick up late-arriving dictionaries.',
parameters: [{"name":"ns","description":"a namespace merged into LocaleNamespaceMap."},{"name":"dicts","description":"complete dictionaries keyed by locale id."}],
parameters: [{ name: 'ns', description: 'a namespace merged into LocaleNamespaceMap.' }, { name: 'dicts', description: 'complete dictionaries keyed by locale id.' }],
returns: 'disposer removing every locale registered by this call (idempotent).',
},
{
signature: 'register(ns: string, locale: string, dict: LocaleDict): () => void',
description: 'Single-locale untyped form for namespaces outside the merge table (dynamic composition, tests).',
parameters: [{"name":"ns","description":"namespace."},{"name":"locale","description":"locale tag."},{"name":"dict","description":"dictionary."}],
parameters: [{ name: 'ns', description: 'namespace.' }, { name: 'locale', description: 'locale tag.' }, { name: 'dict', description: 'dictionary.' }],
returns: 'disposer (idempotent).',
},
{
signature: 'bind<N extends keyof LocaleNamespaceMap & string>(ns: N): TranslateNS<N>',
description: 'Bind a declared namespace to a translate function typed to its dictionary key union (plus the shared common vocabulary) — the same key domain the framework-injected `t` seat carries. The returned reference is stable per namespace (repeat binds return the same function), so it can ride inject surfaces without breaking memoization.',
parameters: [{"name":"ns","description":"a namespace merged into LocaleNamespaceMap."}],
parameters: [{ name: 'ns', description: 'a namespace merged into LocaleNamespaceMap.' }],
returns: 'the typed translate function (reads the active locale at call time).',
},
{
signature: 'bind(ns: string): Translate',
description: 'Untyped form for namespaces outside the merge table (dynamic composition, tests).',
parameters: [{"name":"ns","description":"namespace."}],
parameters: [{ name: 'ns', description: 'namespace.' }],
returns: 'the translate function.',
},
],
@@ -164,47 +164,47 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
{
signature: 'open(id: SessionId): void',
description: 'Select a session as current.',
parameters: [{"name":"id","description":"session id (must exist in the list; unknown ids fail loud)."}],
parameters: [{ name: 'id', description: 'session id (must exist in the list; unknown ids fail loud).' }],
},
{
signature: 'openSubagent(address: SubagentAddress): void',
description: 'Open a healthy catalog child through its exact direct-parent address.',
parameters: [{"name":"address","description":"catalog-derived parent and child ids."}],
parameters: [{ name: 'address', description: 'catalog-derived parent and child ids.' }],
},
{
signature: 'setSubagentCatalogOpen(parentSessionId: SessionId, open: boolean): void',
description: 'Mark whether a catalog menu is consuming live membership updates.',
parameters: [{"name":"parentSessionId","description":"catalog owner."},{"name":"open","description":"current menu state."}],
parameters: [{ name: 'parentSessionId', description: 'catalog owner.' }, { name: 'open', description: 'current menu state.' }],
},
{
signature: 'refreshSubagents(parentSessionId: SessionId): Promise<void>',
description: 'Refresh one direct-child catalog.',
parameters: [{"name":"parentSessionId","description":"catalog owner."}],
parameters: [{ name: 'parentSessionId', description: 'catalog owner.' }],
returns: 'completion of the current or newly started refresh.',
},
{
signature: 'search( query: string, signal: AbortSignal, ): Promise<RpcResult<{ items: SessionSearchResultItem[]; hasMore: boolean }>>',
description: 'Search the Host\'s visible message-content index. Results stay request-local; the list snapshot remains the metadata authority.',
parameters: [{"name":"query","description":"non-blank literal phrase."},{"name":"signal","description":"cancellation for a superseded search."}],
parameters: [{ name: 'query', description: 'non-blank literal phrase.' }, { name: 'signal', description: 'cancellation for a superseded search.' }],
returns: 'bounded results, or a business/transport error.',
},
{
signature: 'fork(opts: { sessionId: SessionId; atSeq?: number; increaseTitle?: boolean }): Promise<SessionId>',
description: 'Fork a session from a completed-turn prefix of the source; on resolution the child is in the list store and `open()` can target it.',
parameters: [{"name":"opts","description":"source session id, the optional event seq anchoring the cut (the boundary is the first turn/end at or after it; an in-log anchor in an open turn is unavailable rather than clipped backward), and whether to increment an inherited durable title before resolving."}],
parameters: [{ name: 'opts', description: 'source session id, the optional event seq anchoring the cut (the boundary is the first turn/end at or after it; an in-log anchor in an open turn is unavailable rather than clipped backward), and whether to increment an inherited durable title before resolving.' }],
returns: 'the child session id.',
throws: ["when the fork fails, or when a requested child-title rename fails after creation."],
throws: ['when the fork fails, or when a requested child-title rename fails after creation.'],
},
{
signature: 'scope(id: SessionId): AgentContext | undefined',
description: 'Resolve an Agent-scoped context view (use-and-discard).',
parameters: [{"name":"id","description":"session id."}],
parameters: [{ name: 'id', description: 'session id.' }],
returns: 'scoped ctx, or undefined for a session neither listed nor already scoped.',
},
{
signature: 'binding(id: SessionId): SessionBinding | undefined',
description: 'Resolve the stable session binding (scope-addressed assembly feed).',
parameters: [{"name":"id","description":"session id."}],
parameters: [{ name: 'id', description: 'session id.' }],
returns: 'binding, or undefined for a session neither listed nor already scoped.',
},
],
@@ -222,9 +222,9 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
{
signature: 'inject(key: keyof SlotMap & string, callback: () => SlotInjectionEffect): () => void',
description: 'Install an effect for each declaration lifetime of a slot. The callback runs synchronously when the declaration already exists; otherwise it runs inside the declaring `register()` call after the declaration is committed. Collapse disposes the effect and a later declaration runs it again. Callback effects are synchronous disposers; iterable effects install transactionally and dispose in reverse order. The controller belongs to the caller\'s fiber, so plugin unload cancels a pending wait and removes any active contribution.',
parameters: [{"name":"key","description":"declared SlotMap key to depend on."},{"name":"callback","description":"creates one disposer or an iterable of disposers."}],
parameters: [{ name: 'key', description: 'declared SlotMap key to depend on.' }, { name: 'callback', description: 'creates one disposer or an iterable of disposers.' }],
returns: 'idempotent disposer for the wait and active effect.',
throws: ["callback setup failures synchronously when the slot is already declared."],
throws: ['callback setup failures synchronously when the slot is already declared.'],
},
],
},
@@ -242,18 +242,18 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
{
signature: 'setTheme(id: string): void',
description: 'Switch the theme preference — the only user preference write entry. Built-in preferences are written through the settings scope and every accepted value emits `theme/change`.',
parameters: [{"name":"id","description":"a registered theme id or `system`; unknown ids throw."}],
parameters: [{ name: 'id', description: 'a registered theme id or `system`; unknown ids throw.' }],
},
{
signature: 'register(definition: ThemeDefinition): () => void',
description: 'Register a theme. Duplicate id throws (single occupant per id; the built-in pair counts; `system` is a preference, not a registrable id).',
parameters: [{"name":"definition","description":"theme id, colorScheme, and alias-token overrides."}],
parameters: [{ name: 'definition', description: 'theme id, colorScheme, and alias-token overrides.' }],
returns: 'disposer. Disposing the theme backing the active preference resets the preference to the default so the UI never keeps tokens of an unregistered theme.',
},
{
signature: 'overrideTokens(source: string, tokens: ThemeTokenOverrides): () => void',
description: 'Stack a token override layer on top of the active theme — the token-level analogue of slot shading: the base theme stays untouched, layers compose in seq order with later layers winning per-token, and removing a layer restores whatever it covered. Calling again with the same source replaces that source\'s whole layer and restacks it on top (effect re-registration semantics). Emits `theme/change` with the recomposed snapshot.',
parameters: [{"name":"source","description":"layer identity; one layer per source (dynamic packages pass their package id — the façade pins it, so it also names the layer's origin for inspection)."},{"name":"tokens","description":"token-name → `{ light, dark }` value pairs. Validated at runtime (model-authored callers reach this boundary with untyped JS); a bare string value throws a teaching error."}],
parameters: [{ name: 'source', description: 'layer identity; one layer per source (dynamic packages pass their package id — the façade pins it, so it also names the layer\'s origin for inspection).' }, { name: 'tokens', description: 'token-name → `{ light, dark }` value pairs. Validated at runtime (model-authored callers reach this boundary with untyped JS); a bare string value throws a teaching error.' }],
returns: 'disposer removing exactly the layer this call created; a no-op once the source has re-overridden (the newer layer is not torn down).',
},
],
@@ -303,18 +303,18 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
{
signature: 'connectWorkspace(workspaceId: WorkspaceId): Promise<SessionId>',
description: 'Connect a Workspace to its reusable or freshly created blank session.',
parameters: [{"name":"workspaceId","description":"target workspace."}],
parameters: [{ name: 'workspaceId', description: 'target workspace.' }],
returns: 'the connected session id.',
},
{
signature: 'startSession(workspaceId?: WorkspaceId): void',
description: 'The New Session flow: connect the target (or recent) Workspace and open the resulting session; failures surface on the session list state.',
parameters: [{"name":"workspaceId","description":"explicit target; omitted uses the recency projection."}],
description: 'The New Session flow: connect the explicit, current-Session, or recent Workspace and open the resulting session; failures surface on the session list state.',
parameters: [{ name: 'workspaceId', description: 'explicit target; omitted inherits the current Session\'s Workspace before falling back to the recency projection.' }],
},
{
signature: 'create(input: { path: string }): Promise<WorkspaceView>',
description: 'Register an existing path as a Workspace.',
parameters: [{"name":"input","description":"the Host create payload."}],
parameters: [{ name: 'input', description: 'the Host create payload.' }],
returns: 'the created or idempotently resolved Workspace.',
},
{
@@ -326,41 +326,41 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
{
signature: 'listDirectory(path?: string, signal?: AbortSignal): Promise<DirectoryListing>',
description: 'List one directory level through the Host\'s `browse` capability.',
parameters: [{"name":"path","description":"absolute directory to list; absent lists the Host home directory."},{"name":"signal","description":"aborts the wire request (and the Host's scan) when the caller supersedes it."}],
parameters: [{ name: 'path', description: 'absolute directory to list; absent lists the Host home directory.' }, { name: 'signal', description: 'aborts the wire request (and the Host\'s scan) when the caller supersedes it.' }],
returns: 'the level\'s listing with breadcrumb ancestry.',
},
{
signature: 'createDirectory(path: string, name: string): Promise<string>',
description: 'Create one child directory through the Host\'s `browse` capability.',
parameters: [{"name":"path","description":"absolute existing parent directory."},{"name":"name","description":"single non-blank path segment."}],
parameters: [{ name: 'path', description: 'absolute existing parent directory.' }, { name: 'name', description: 'single non-blank path segment.' }],
returns: 'the created directory\'s absolute path.',
},
{
signature: 'openPath(path: string): Promise<void>',
description: 'Open a filesystem path with the Host operating system\'s default application.',
parameters: [{"name":"path","description":"absolute or host-resolvable path."}],
parameters: [{ name: 'path', description: 'absolute or host-resolvable path.' }],
},
{
signature: 'rename(workspaceId: WorkspaceId, title: string): Promise<WorkspaceView>',
description: 'Rename a Workspace.',
parameters: [{"name":"workspaceId","description":"target workspace."},{"name":"title","description":"the new display title."}],
parameters: [{ name: 'workspaceId', description: 'target workspace.' }, { name: 'title', description: 'the new display title.' }],
returns: 'the updated Workspace view.',
},
{
signature: 'delete(workspaceId: WorkspaceId): Promise<void>',
description: 'Delete a Workspace (its sessions fall back to the unaccounted group).',
parameters: [{"name":"workspaceId","description":"target workspace."}],
parameters: [{ name: 'workspaceId', description: 'target workspace.' }],
},
{
signature: 'insertSessionBefore(workspaceId: WorkspaceId, sessionId: SessionId, beforeSessionId?: SessionId): Promise<WorkspaceView>',
description: 'Move an accounted session within/into a Workspace\'s ordered list.',
parameters: [{"name":"workspaceId","description":"target workspace."},{"name":"sessionId","description":"accounted session to move."},{"name":"beforeSessionId","description":"accounted anchor to insert before; omitted appends."}],
parameters: [{ name: 'workspaceId', description: 'target workspace.' }, { name: 'sessionId', description: 'accounted session to move.' }, { name: 'beforeSessionId', description: 'accounted anchor to insert before; omitted appends.' }],
returns: 'the updated Workspace view.',
},
{
signature: 'archiveSession(sessionId: SessionId): Promise<void>',
description: 'Archive a session into the registry-global set (hidden from grouping surfaces; session log and accounting slot remain). Archiving the current session clears the selection into the New Session view state.',
parameters: [{"name":"sessionId","description":"session to archive."}],
parameters: [{ name: 'sessionId', description: 'session to archive.' }],
},
],
},
@@ -382,7 +382,7 @@ export const EVENT_API: readonly EventApiEntry[] = [
signature: '\'locale/change\'(snapshot: LocaleSnapshot): void',
summary: 'The active locale switched.',
description: 'The active locale switched. Dictionary registrations do NOT emit this event (listeners may re-register slots in response, and boot registers one namespace per package); continuous render refresh rides the LocaleFace revision instead.',
parameters: [{"name":"snapshot","description":"Current immutable locale snapshot."}],
parameters: [{ name: 'snapshot', description: 'Current immutable locale snapshot.' }],
},
{
name: 'slots/changed',
@@ -390,7 +390,7 @@ export const EVENT_API: readonly EventApiEntry[] = [
signature: '\'slots/changed\'(key: string): void',
summary: 'A slot\'s definition or registration set changed.',
description: 'A slot\'s definition or registration set changed.',
parameters: [{"name":"key","description":"the mutated SlotMap key."}],
parameters: [{ name: 'key', description: 'the mutated SlotMap key.' }],
},
{
name: 'theme/change',
@@ -398,7 +398,7 @@ export const EVENT_API: readonly EventApiEntry[] = [
signature: '\'theme/change\'(snapshot: ThemeSnapshot): void',
summary: 'Theme state changed (preference switched, registry updated, or the OS color scheme changed while the preference is `system`).',
description: 'Theme state changed (preference switched, registry updated, or the OS color scheme changed while the preference is `system`).',
parameters: [{"name":"snapshot","description":"Current immutable theme snapshot."}],
parameters: [{ name: 'snapshot', description: 'Current immutable theme snapshot.' }],
},
]
@@ -418,7 +418,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
},
{
name: 'AssistantMessageNode',
declaration: 'export interface AssistantMessageNode {\n kind: \'assistant\';\n seq: number;\n time: number;\n turn: number;\n step: number;\n blocks: readonly AssistantBlock[];\n usage?: unknown;\n provenance?: AssistantProvenanceView;\n requestConfig?: AssistantRequestConfig;\n timing?: AssistantTiming;\n interrupted?: true;\n}',
declaration: 'export interface AssistantMessageNode {\n kind: \'assistant\';\n seq: number;\n messageId?: MessageId;\n time: number;\n turn: number;\n step: number;\n blocks: readonly AssistantBlock[];\n usage?: unknown;\n provenance?: AssistantProvenanceView;\n requestConfig?: AssistantRequestConfig;\n timing?: AssistantTiming;\n interrupted?: true;\n}',
},
{
name: 'AssistantProvenanceView',
@@ -510,7 +510,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
},
{
name: 'ConversationNode',
declaration: 'export type ConversationNode = UserMessageNode | AssistantMessageNode | SteeringMessageNode | ContextMessageNode | ModelRetryNode | TurnErrorNode | ToolResultNode | CommandNode | CompactionSummaryNode | UnknownSurfaceNode;',
declaration: 'export type ConversationNode = UserMessageNode | AssistantMessageNode | SteeringMessageNode | ContextMessageNode | ModelRetryNode | TurnErrorNode | TurnMaxTokensNode | ToolResultNode | CommandNode | CompactionSummaryNode | UnknownSurfaceNode;',
},
{
name: 'ConversationSnapshot',
@@ -852,6 +852,10 @@ export const TYPE_API: readonly TypeApiEntry[] = [
name: 'TurnLocation',
declaration: 'export interface TurnLocation {\n readonly turn: number;\n readonly start: SessionEvent<\'turn/start\'> | undefined;\n readonly end: SessionEvent<\'turn/end\'> | undefined;\n readonly status: \'open\' | \'closed\' | \'unknown\';\n readonly steps: readonly StepLocation[];\n readonly data: ConversationLocationDataStore<ConversationTurnDataMap>;\n}',
},
{
name: 'TurnMaxTokensNode',
declaration: 'export interface TurnMaxTokensNode {\n kind: \'turn-max-tokens\';\n seq: number;\n time: number;\n turn: number;\n step: number;\n}',
},
{
name: 'UnknownSurfaceNode',
declaration: 'export interface UnknownSurfaceNode {\n kind: \'unknown\';\n seq: number;\n time: number;\n type: string;\n data: unknown;\n}',

View File

@@ -140,7 +140,11 @@ declare module '@deepseek-ai/cordis' {
}
}
/** Provide the registry as a normal Client service. */
/**
* Provide the registry as a normal Client service.
* @param ctx - Client Cordis context receiving the service.
* @param registry - page-local inspect registry to publish.
*/
export function provideClientCordisInspect(ctx: Context, registry: ClientCordisInspectRegistry): void {
ctx.provide('cordisInspect', registry)
}

View File

@@ -142,7 +142,10 @@ export class CordisRunOrchestrator {
subscribe: fn => this.observe(fn),
}
/** Register a Client activation request, starting it immediately when the Plugin is already authorized. */
/**
* Register a Client activation request, starting it immediately when the Plugin is already authorized.
* @param request - forwarded approval and activation metadata.
*/
open(request: CordisRunRequest): void {
this.requests.set(request.requestId, request)
if (!request.requiresApproval) {
@@ -238,7 +241,10 @@ export class CordisRunOrchestrator {
if (changed) this.commit()
}
/** Close an approval settled by another page or by cancellation. */
/**
* Close an approval settled by another page or by cancellation.
* @param requestId - approval request that can no longer be answered here.
*/
close(requestId: ApprovalRequestId): void {
const request = this.requests.get(requestId)
if (request === undefined) return
@@ -250,7 +256,11 @@ export class CordisRunOrchestrator {
this.commit()
}
/** Approve and execute one still-open model request. */
/**
* Approve and execute one still-open model request.
* @param requestId - approval request to execute.
* @param approveFutureVersions - whether this approval covers later Packages for the same Plugin.
*/
approve(requestId: ApprovalRequestId, approveFutureVersions: boolean): Promise<void> {
const request = this.requests.get(requestId)
if (request === undefined || !request.requiresApproval) return Promise.resolve()
@@ -265,7 +275,10 @@ export class CordisRunOrchestrator {
})
}
/** Reject one still-open model request without executing either half. */
/**
* Reject one still-open model request without executing either half.
* @param requestId - approval request to reject.
*/
async decline(requestId: ApprovalRequestId): Promise<void> {
const request = this.requests.get(requestId)
if (request === undefined || !request.requiresApproval) return
@@ -277,7 +290,10 @@ export class CordisRunOrchestrator {
await this.answer(requestId, { ok: false, reason: 'rejected' })
}
/** Execute a direct panel run; the user gesture itself authorizes it. */
/**
* Execute a direct panel run; the user gesture itself authorizes it.
* @param request - exact Package activation selected by the user.
*/
startUserRun(request: CordisUserRunRequest): Promise<void> {
return this.orchestrate(request)
}

View File

@@ -67,7 +67,11 @@ export const CLIENT_BUILTIN_INSPECTION: readonly JsonValue[] = [
},
]
/** Construct the first-party Client provider registrations. */
/**
* Construct the first-party Client provider registrations.
* @param ctx - Client context used for live Service-backed queries.
* @returns registrations for static catalogs and live Client capabilities.
*/
export function clientInspectProviders(ctx: Context): ClientCordisInspectProviderRegistration[] {
return [
registration(

View File

@@ -110,6 +110,58 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation\', () => ctx.slots.register(\n { name: \'conversation\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-layout/src/client/index.ts:62',
},
{
key: 'conversation.chat.assistant-actions',
kind: 'list',
scope: 'session',
summary: 'Action strip attached to one finalized assistant message, rendered inside that message\'s IconActions row.',
doc: 'Action strip attached to one finalized assistant message, rendered\ninside that message\'s IconActions row. The chat entry owns the render\nsite and passes the addressed message identity; contributors add\nper-message actions without importing the conversation implementation.\nEntries render by ascending `order`.',
registerOptions: [
{
name: 'id',
requirement: 'required',
type: 'string',
doc: 'Your cell key. Use an id of your own: a fresh id is added beside the shipped entries, while reusing a shipped id puts you in THAT cell and replaces it. Owners that filter by id address you by it.',
},
{
name: 'order',
requirement: 'optional',
type: 'number',
doc: 'Position among the entries, ascending (default 0).',
},
{
name: 'label',
requirement: 'optional',
type: 'string | (() => string)',
doc: 'Display text where the owner projects one (nav rows, tabs). A thunk is re-read on every projection, so localized text follows the active locale without re-registering.',
},
],
ownerProps: [
'/**\n * Owner currency of the assistant-message action strip: the durable identity\n * of the one finalized message the contributed actions address. Only finalized\n * messages reach this slot, so the id is always present.\n */\nexport interface AssistantActionOwnerProps {\n /** Stable identity carried from the `assistant/message` event. */\n messageId: MessageId\n}',
],
ownerPropsReferences: [
'MessageId',
],
standardProps: [
'useSessions: SnapshotSelectorHook<SessionListState>',
'useWorkspaces: SnapshotSelectorHook<import(\'./workspaces/service.ts\').WorkspaceListState>',
'useSession: SnapshotSelectorHook<ConversationSnapshot>',
'sessionId: SessionId',
'useProjection: UseProjection',
'useInput: SnapshotSelectorHook<InputState>',
'inputActions: InputActions',
],
keyDomain: '',
hookContext: '',
slotInject: '',
declaredBy: 'an entry in \'conversation.chat.node\' (client-ui-conversation), so it exists while that entry is mounted',
occupants: [
'client-ui-feedback FeedbackActions id \'feedback\'',
],
replaceRisk: 'none',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.chat.assistant-actions\', () => ctx.slots.register(\n { name: \'conversation.chat.assistant-actions\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:109',
},
{
key: 'conversation.chat.commandview',
kind: 'keyed',
@@ -147,7 +199,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
occupants: [],
replaceRisk: 'none',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.chat.commandview\', () => ctx.slots.register(\n { name: \'conversation.chat.commandview\', key: \'<one key the owner dispatches>\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:88',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:94',
},
{
key: 'conversation.chat.node',
@@ -180,7 +232,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
'useInput: SnapshotSelectorHook<InputState>',
'inputActions: InputActions',
],
keyDomain: 'fixed by the owner\'s key table { [Kind in ChatNodeKind]: { node: ChatNode<Kind> } }, already taken: assistant-step, command, command-input, compaction, context, manual-compaction, model-retry, steering, tool-call, turn-error, turn-tail, unknown, user, workflow-run',
keyDomain: 'fixed by the owner\'s key table { [Kind in ChatNodeKind]: { node: ChatNode<Kind> } }, already taken: assistant-step, command, command-input, compaction, context, manual-compaction, model-retry, steering, tool-call, turn-error, turn-max-tokens, turn-tail, unknown, user, workflow-run',
hookContext: 'string',
slotInject: 'ChatNodeTurnDataInjected',
declaredBy: 'an entry in \'conversation.view\' (client-ui-conversation), so it exists while that entry is mounted',
@@ -194,6 +246,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
'client-ui-conversation CompactionNodeView key \'compaction\'',
'client-ui-conversation RetryNodeView key \'model-retry\'',
'client-ui-conversation TurnErrorNodeView key \'turn-error\'',
'client-ui-conversation TurnMaxTokensNodeView key \'turn-max-tokens\'',
'client-ui-conversation TurnTailNodeView key \'turn-tail\'',
'client-ui-conversation UnknownNodeView key \'unknown\'',
'client-ui-goal GoalCommandInputView key \'command-input\'',
@@ -202,7 +255,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
],
replaceRisk: 'shadows-shipped-ui',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.chat.node\', () => ctx.slots.register(\n { name: \'conversation.chat.node\', key: \'<one key the owner dispatches>\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:72',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:78',
},
{
key: 'conversation.chat.turnTail',
@@ -242,7 +295,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
],
replaceRisk: 'none',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.chat.turnTail\', () => ctx.slots.register(\n { name: \'conversation.chat.turnTail\', select: owner => null },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:95',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:101',
},
{
key: 'conversation.composer',
@@ -285,7 +338,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
],
replaceRisk: 'none',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.composer\', () => ctx.slots.register(\n { name: \'conversation.composer\', select: owner => null },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:114',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:132',
},
{
key: 'conversation.composer.bar',
@@ -318,7 +371,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
],
replaceRisk: 'shadows-shipped-ui',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.composer.bar\', () => ctx.slots.register(\n { name: \'conversation.composer.bar\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:183',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:201',
},
{
key: 'conversation.composer.dock',
@@ -371,7 +424,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
],
replaceRisk: 'none',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.composer.dock\', () => ctx.slots.register(\n { name: \'conversation.composer.dock\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:152',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:170',
},
{
key: 'conversation.details.tool',
@@ -402,7 +455,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
],
replaceRisk: 'shadows-shipped-ui',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.details.tool\', () => ctx.slots.register(\n { name: \'conversation.details.tool\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:106',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:124',
},
{
key: 'conversation.hero.agentPreset',
@@ -428,7 +481,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
],
replaceRisk: 'shadows-shipped-ui',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.hero.agentPreset\', () => ctx.slots.register(\n { name: \'conversation.hero.agentPreset\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:127',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:145',
},
{
key: 'conversation.hero.workspace',
@@ -456,7 +509,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
],
replaceRisk: 'shadows-shipped-ui',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.hero.workspace\', () => ctx.slots.register(\n { name: \'conversation.hero.workspace\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:121',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:139',
},
{
key: 'conversation.hero.workspace.directoryFlow',
@@ -538,7 +591,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
],
replaceRisk: 'none',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.input.dock\', () => ctx.slots.register(\n { name: \'conversation.input.dock\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:143',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:161',
},
{
key: 'conversation.input.left',
@@ -589,7 +642,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
occupants: [],
replaceRisk: 'none',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.input.left\', () => ctx.slots.register(\n { name: \'conversation.input.left\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:161',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:179',
},
{
key: 'conversation.input.model',
@@ -620,7 +673,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
],
replaceRisk: 'shadows-shipped-ui',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.input.model\', () => ctx.slots.register(\n { name: \'conversation.input.model\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:203',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:221',
},
{
key: 'conversation.input.overlay',
@@ -700,7 +753,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
],
replaceRisk: 'shadows-shipped-ui',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.input.plan\', () => ctx.slots.register(\n { name: \'conversation.input.plan\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:193',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:211',
},
{
key: 'conversation.input.right',
@@ -751,7 +804,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
occupants: [],
replaceRisk: 'none',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.input.right\', () => ctx.slots.register(\n { name: \'conversation.input.right\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:169',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:187',
},
{
key: 'conversation.session',
@@ -780,7 +833,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
],
replaceRisk: 'shadows-shipped-ui',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.session\', () => ctx.slots.register(\n { name: \'conversation.session\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:43',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:44',
},
{
key: 'conversation.session.header',
@@ -809,7 +862,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
],
replaceRisk: 'shadows-shipped-ui',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.session.header\', () => ctx.slots.register(\n { name: \'conversation.session.header\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:51',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:52',
},
{
key: 'conversation.session.header.actions',
@@ -861,7 +914,57 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
],
replaceRisk: 'none',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.session.header.actions\', () => ctx.slots.register(\n { name: \'conversation.session.header.actions\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:62',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:63',
},
{
key: 'conversation.session.header.utilities',
kind: 'list',
scope: 'session',
summary: 'Right-aligned Session utilities kept outside the title-adjacent action group, so an optional utility cannot reorder session context or lineage.',
doc: 'Right-aligned Session utilities kept outside the title-adjacent action\ngroup, so an optional utility cannot reorder session context or lineage.',
registerOptions: [
{
name: 'id',
requirement: 'required',
type: 'string',
doc: 'Your cell key. Use an id of your own: a fresh id is added beside the shipped entries, while reusing a shipped id puts you in THAT cell and replaces it. Owners that filter by id address you by it.',
},
{
name: 'order',
requirement: 'optional',
type: 'number',
doc: 'Position among the entries, ascending (default 0).',
},
{
name: 'label',
requirement: 'optional',
type: 'string | (() => string)',
doc: 'Display text where the owner projects one (nav rows, tabs). A thunk is re-read on every projection, so localized text follows the active locale without re-registering.',
},
],
ownerProps: [
'/** Header actions derive their state from the standard session/global kit. */\nexport interface ConversationHeaderActionOwnerProps {}',
],
ownerPropsReferences: [],
standardProps: [
'useSessions: SnapshotSelectorHook<SessionListState>',
'useWorkspaces: SnapshotSelectorHook<import(\'./workspaces/service.ts\').WorkspaceListState>',
'useSession: SnapshotSelectorHook<ConversationSnapshot>',
'sessionId: SessionId',
'useProjection: UseProjection',
'useInput: SnapshotSelectorHook<InputState>',
'inputActions: InputActions',
],
keyDomain: '',
hookContext: '',
slotInject: '',
declaredBy: 'an entry in \'conversation.session.header\' (client-ui-conversation), so it exists while that entry is mounted',
occupants: [
'session-export SessionExportHeader id \'session-export\'',
],
replaceRisk: 'none',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.session.header.utilities\', () => ctx.slots.register(\n { name: \'conversation.session.header.utilities\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:68',
},
{
key: 'conversation.view',
@@ -912,7 +1015,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
],
replaceRisk: 'none',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'conversation.view\', () => ctx.slots.register(\n { name: \'conversation.view\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:70',
source: 'packages/client/ui-conversation/src/client/contract/slots.ts:76',
},
{
key: 'details',
@@ -1089,7 +1192,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
],
replaceRisk: 'none',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'settings.general.item\', () => ctx.slots.register(\n { name: \'settings.general.item\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-settings/src/client/contract/slots.ts:81',
source: 'packages/client/ui-settings/src/client/contract/slots.ts:90',
},
{
key: 'settings.header',
@@ -1161,7 +1264,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
],
replaceRisk: 'none',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'settings.onboarding\', () => ctx.slots.register(\n { name: \'settings.onboarding\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-settings/src/client/contract/slots.ts:66',
source: 'packages/client/ui-settings/src/client/contract/slots.ts:75',
},
{
key: 'settings.plugin.item',
@@ -1200,7 +1303,7 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
keyDomain: '',
hookContext: '',
slotInject: '',
declaredBy: 'an entry in \'settings.section\' (client-ui-plugin-config), so it exists while that entry is mounted',
declaredBy: 'an entry in \'settings.plugins.tab\' (client-ui-plugin-config), so it exists while that entry is mounted',
occupants: [
'client-ui-plugin-config BashCard id \'bash\'',
'client-ui-plugin-config AgentLoopCard id \'agent-loop\'',
@@ -1210,6 +1313,52 @@ export const CLIENT_SLOT_API: readonly ClientSlotEntry[] = [
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'settings.plugin.item\', () => ctx.slots.register(\n { name: \'settings.plugin.item\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-plugin-config/src/client/slot-contract.ts:16',
},
{
key: 'settings.plugins.tab',
kind: 'list',
scope: 'root',
summary: 'One page inside the Plugins settings section.',
doc: 'One page inside the Plugins settings section. The section owner renders\nlocalized entry labels as tabs and mounts each contribution inside its\ncorresponding tab panel. Options: `id` (tab key), `order` (tab order),\nand `label` (registrant-localized tab text). Declared at runtime by the\nfeature that owns the Plugins section; the type lives here so inventory\nand configuration plugins collaborate without depending on one another.',
registerOptions: [
{
name: 'id',
requirement: 'required',
type: 'string',
doc: 'Your cell key. Use an id of your own: a fresh id is added beside the shipped entries, while reusing a shipped id puts you in THAT cell and replaces it. Owners that filter by id address you by it.',
},
{
name: 'order',
requirement: 'optional',
type: 'number',
doc: 'Position among the entries, ascending (default 0).',
},
{
name: 'label',
requirement: 'optional',
type: 'string | (() => string)',
doc: 'Display text where the owner projects one (nav rows, tabs). A thunk is re-read on every projection, so localized text follows the active locale without re-registering.',
},
],
ownerProps: [
'/** Owner share of a Plugins tab (the section supplies nothing). */\nexport interface SettingsPluginsTabOwnerProps {\n /** Marker field: tab owner props are intentionally empty. */\n children?: never\n}',
],
ownerPropsReferences: [],
standardProps: [
'useSessions: SnapshotSelectorHook<SessionListState>',
'useWorkspaces: SnapshotSelectorHook<import(\'./workspaces/service.ts\').WorkspaceListState>',
],
keyDomain: '',
hookContext: '',
slotInject: '',
declaredBy: 'an entry in \'settings.section\' (client-ui-plugin-config), so it exists while that entry is mounted',
occupants: [
'client-ui-plugin-config ConfigurablePluginsTab id \'configurable\'',
'client-ui-plugins PluginSettingsSection id \'all\'',
],
replaceRisk: 'none',
example: 'return {\n inject: [\'slots\'],\n apply(ctx) {\n ctx.slots.inject(\'settings.plugins.tab\', () => ctx.slots.register(\n { name: \'settings.plugins.tab\', id: \'my-entry\', order: 100, label: \'My entry\' },\n () => React.createElement(\'div\', null, \'hello\'),\n ))\n },\n}',
source: 'packages/client/ui-settings/src/client/contract/slots.ts:62',
},
{
key: 'settings.section',
kind: 'list',