fix: test/docs
This commit is contained in:
@@ -10,4 +10,3 @@ Model-facing tools over the live cordis runtime the agent itself runs inside: in
|
||||
| [`cordis-host-runner/`](cordis-host-runner/README.md) | Definition registry, the `node:vm` sandbox for host halves, and the request-run round trip | provides `ctx.dynamicCordisRunner` |
|
||||
| [`cordis-client-runner/`](cordis-client-runner/README.md) | Browser half of a dual-half package: evaluates the definition into a live browser plugin and answers the run request | client face; provides the browser `ctx.dynamicCordisRunner` |
|
||||
| [`ui-cordis/`](ui-cordis/README.md) | Browser surfaces: the frame-wide panel that operates every definition, and the read-only define card | client face; registers slots |
|
||||
| [`repository-plugin/`](repository-plugin/README.md) | Repository skill and MCP composition | registers a Loader builtin |
|
||||
@@ -10,4 +10,3 @@ agent 修改自身运行时:检查已加载的插件与服务接口、定义
|
||||
| [`cordis-host-runner/`](cordis-host-runner/README.md) | 定义注册表、host 半的 `node:vm` 沙箱,以及 request-run 往返 | 提供 `ctx.dynamicCordisRunner` |
|
||||
| [`cordis-client-runner/`](cordis-client-runner/README.md) | 双半包的浏览器半:把定义求值成活的浏览器插件,并应答运行请求 | client 面;提供浏览器侧 `ctx.dynamicCordisRunner` |
|
||||
| [`ui-cordis/`](ui-cordis/README.md) | 浏览器面:操作全部定义的全局面板,与只读的 define 卡片 | client 面;注册 slot |
|
||||
| [`repository-plugin/`](repository-plugin/README.md) | 通过 DSH 自有子 Plugin 准备并挂载静态 repository skills 与通用 `.mcp.json` server | 注册一个 Loader builtin |
|
||||
@@ -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}',
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -45,9 +45,7 @@
|
||||
"lib/typert.host.js",
|
||||
"lib/typert.host.d.ts",
|
||||
"lib/typert.remote-client.js",
|
||||
"lib/typert.remote-client.d.ts",
|
||||
"lib/typert.remote-client.d.ts.map",
|
||||
"src"
|
||||
"lib/typert.remote-client.d.ts"
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
|
||||
@@ -41,22 +41,38 @@ export { CordisInspectRegistryService } from './inspect-registry.ts'
|
||||
export type { HostCordisInspectProviderRegistration } from './inspect-registry.ts'
|
||||
export { HOST_BUILTIN_INSPECTION } from './sandbox.ts'
|
||||
|
||||
/** Brand a Host-minted Plugin ID. */
|
||||
/**
|
||||
* Brand a Host-minted Plugin ID.
|
||||
* @param id - opaque identifier minted by the Host registry.
|
||||
* @returns the branded Plugin identifier.
|
||||
*/
|
||||
export function CordisDynamicPluginId(id: string): CordisDynamicPluginId {
|
||||
return id as CordisDynamicPluginId
|
||||
}
|
||||
|
||||
/** Brand a Host-minted Package ID. */
|
||||
/**
|
||||
* Brand a Host-minted Package ID.
|
||||
* @param id - opaque identifier minted by the Host registry.
|
||||
* @returns the branded Package identifier.
|
||||
*/
|
||||
export function CordisDynamicPackageId(id: string): CordisDynamicPackageId {
|
||||
return id as CordisDynamicPackageId
|
||||
}
|
||||
|
||||
/** Brand a Host-minted Plugin Run ID. */
|
||||
/**
|
||||
* Brand a Host-minted Plugin Run ID.
|
||||
* @param id - opaque identifier minted by the Host registry.
|
||||
* @returns the branded Plugin Run identifier.
|
||||
*/
|
||||
export function CordisDynamicPluginRunId(id: string): CordisDynamicPluginRunId {
|
||||
return id as CordisDynamicPluginRunId
|
||||
}
|
||||
|
||||
/** Brand a Host-minted approval request ID. */
|
||||
/**
|
||||
* Brand a Host-minted approval request ID.
|
||||
* @param id - opaque identifier minted by the Host registry.
|
||||
* @returns the branded approval request identifier.
|
||||
*/
|
||||
export function ApprovalRequestId(id: string): ApprovalRequestId {
|
||||
return id as ApprovalRequestId
|
||||
}
|
||||
|
||||
@@ -146,7 +146,11 @@ export class DynamicCordisRegistry {
|
||||
private nextRun = 1
|
||||
private nextApproval = 1
|
||||
|
||||
/** Mint a semantic plugin ID without reusing a prior suffix. */
|
||||
/**
|
||||
* Mint a semantic plugin ID without reusing a prior suffix.
|
||||
* @param prefix - validated lowercase semantic prefix proposed by the model.
|
||||
* @returns a process-unique Plugin ID.
|
||||
*/
|
||||
mintPluginId(prefix: string): string {
|
||||
let id: CordisDynamicPluginId
|
||||
do id = `${prefix}-${this.nextPlugin++}` as CordisDynamicPluginId
|
||||
@@ -154,69 +158,115 @@ export class DynamicCordisRegistry {
|
||||
return id
|
||||
}
|
||||
|
||||
/** Mint an immutable package ID. */
|
||||
/**
|
||||
* Mint an immutable package ID.
|
||||
* @returns a process-unique Package ID.
|
||||
*/
|
||||
mintPackageId(): string {
|
||||
return `pkg-${this.nextPackage++}`
|
||||
}
|
||||
|
||||
/** Mint an activation ID. */
|
||||
/**
|
||||
* Mint an activation ID.
|
||||
* @returns a process-unique Plugin Run ID.
|
||||
*/
|
||||
mintPluginRunId(): string {
|
||||
return `run-${this.nextRun++}`
|
||||
}
|
||||
|
||||
/** Mint an approval ID. */
|
||||
/**
|
||||
* Mint an approval ID.
|
||||
* @returns a process-unique approval request ID.
|
||||
*/
|
||||
mintApprovalRequestId(): string {
|
||||
return `approval-${this.nextApproval++}`
|
||||
}
|
||||
|
||||
/** Add one stable plugin. */
|
||||
/**
|
||||
* Add one stable plugin.
|
||||
* @param plugin - Plugin record to retain under its stable ID.
|
||||
*/
|
||||
add(plugin: DynamicCordisPlugin): void {
|
||||
this.plugins.set(plugin.pluginId, plugin)
|
||||
}
|
||||
|
||||
/** Read one plugin. */
|
||||
/**
|
||||
* Read one plugin.
|
||||
* @param id - stable Plugin ID.
|
||||
* @returns the Plugin record, or `undefined` when absent.
|
||||
*/
|
||||
get(id: CordisDynamicPluginId): DynamicCordisPlugin | undefined {
|
||||
return this.plugins.get(id)
|
||||
}
|
||||
|
||||
/** Delete one plugin and all package versions. */
|
||||
/**
|
||||
* Delete one plugin and all package versions.
|
||||
* @param id - stable Plugin ID to remove.
|
||||
* @returns whether a Plugin record was removed.
|
||||
*/
|
||||
delete(id: CordisDynamicPluginId): boolean {
|
||||
return this.plugins.delete(id)
|
||||
}
|
||||
|
||||
/** All plugins in creation order. */
|
||||
/**
|
||||
* Read all plugins in creation order.
|
||||
* @returns a snapshot of every Plugin record.
|
||||
*/
|
||||
all(): DynamicCordisPlugin[] {
|
||||
return [...this.plugins.values()]
|
||||
}
|
||||
|
||||
/** One session's plugins in creation order. */
|
||||
/**
|
||||
* Read one session's plugins in creation order.
|
||||
* @param sessionId - owning session to filter by.
|
||||
* @returns a snapshot of matching Plugin records.
|
||||
*/
|
||||
ofSession(sessionId: SessionId): DynamicCordisPlugin[] {
|
||||
return this.all().filter(plugin => plugin.sessionId === sessionId)
|
||||
}
|
||||
|
||||
/** Publish one pending approval. */
|
||||
/**
|
||||
* Publish one pending approval.
|
||||
* @param id - approval request ID.
|
||||
* @param pending - resolver and Plugin metadata retained until settlement.
|
||||
*/
|
||||
armRequest(id: ApprovalRequestId, pending: DynamicCordisPendingRequest): void {
|
||||
this.pendingRequests.set(id, pending)
|
||||
}
|
||||
|
||||
/** Read one pending approval without claiming it. */
|
||||
/**
|
||||
* Read one pending approval without claiming it.
|
||||
* @param id - approval request ID.
|
||||
* @returns the pending request, or `undefined` when absent.
|
||||
*/
|
||||
peekRequest(id: ApprovalRequestId): DynamicCordisPendingRequest | undefined {
|
||||
return this.pendingRequests.get(id)
|
||||
}
|
||||
|
||||
/** Claim one pending approval; first answer wins. */
|
||||
/**
|
||||
* Claim one pending approval; first answer wins.
|
||||
* @param id - approval request ID.
|
||||
* @returns the claimed request, or `undefined` when already settled.
|
||||
*/
|
||||
claimRequest(id: ApprovalRequestId): DynamicCordisPendingRequest | undefined {
|
||||
const pending = this.pendingRequests.get(id)
|
||||
if (pending !== undefined) this.pendingRequests.delete(id)
|
||||
return pending
|
||||
}
|
||||
|
||||
/** Cancel one pending approval. */
|
||||
/**
|
||||
* Cancel one pending approval.
|
||||
* @param id - approval request ID to remove.
|
||||
*/
|
||||
disarmRequest(id: ApprovalRequestId): void {
|
||||
this.pendingRequests.delete(id)
|
||||
}
|
||||
|
||||
/** Pending approval for one plugin, if any. */
|
||||
/**
|
||||
* Find a pending approval for one Plugin.
|
||||
* @param pluginId - stable Plugin ID.
|
||||
* @returns its approval request ID, or `undefined` when none is pending.
|
||||
*/
|
||||
pendingRequestFor(pluginId: CordisDynamicPluginId): ApprovalRequestId | undefined {
|
||||
for (const [requestId, request] of this.pendingRequests) {
|
||||
if (request.pluginId === pluginId) return requestId
|
||||
|
||||
@@ -2,23 +2,38 @@
|
||||
|
||||
import type { GenericCallView } from '@deepseek-ai/dsh-tools'
|
||||
|
||||
/** Render a runtime-inspection call. */
|
||||
/**
|
||||
* Render a runtime-inspection call.
|
||||
* @param args - requested runtime category and optional member name.
|
||||
* @returns replay-safe generic call presentation.
|
||||
*/
|
||||
export function presentRuntimeInspectCall(args: { what?: string; name?: string }): GenericCallView {
|
||||
const target = args.name === undefined ? args.what : `${args.what}: ${args.name}`
|
||||
return { card: 'generic', kind: 'read', title: target === undefined ? 'Inspect Cordis runtime' : `Inspect Cordis runtime: ${target}` }
|
||||
}
|
||||
|
||||
/** Render provider-directory inspection. */
|
||||
/**
|
||||
* Render provider-directory inspection.
|
||||
* @returns replay-safe generic call presentation.
|
||||
*/
|
||||
export function presentInspectListCall(): GenericCallView {
|
||||
return { card: 'generic', kind: 'read', title: 'List Cordis Inspect Providers' }
|
||||
}
|
||||
|
||||
/** Render one provider query. */
|
||||
/**
|
||||
* Render one provider query.
|
||||
* @param args - target platform, provider, and method.
|
||||
* @returns replay-safe generic call presentation.
|
||||
*/
|
||||
export function presentInspectQueryCall(args: { platform: string; provider: string; method: string }): GenericCallView {
|
||||
return { card: 'generic', kind: 'read', title: `Query Cordis ${args.platform} ${args.provider}.${args.method}` }
|
||||
}
|
||||
|
||||
/** Render layered self-inspection. */
|
||||
/**
|
||||
* Render layered self-inspection.
|
||||
* @param args - optional Plugin and Package identity.
|
||||
* @returns replay-safe generic call presentation.
|
||||
*/
|
||||
export function presentInspectSelfCall(args: { pluginId?: string; packageId?: string }): GenericCallView {
|
||||
const target = args.pluginId === undefined
|
||||
? 'dynamic Cordis Plugins'
|
||||
@@ -26,12 +41,20 @@ export function presentInspectSelfCall(args: { pluginId?: string; packageId?: st
|
||||
return { card: 'generic', kind: 'read', title: `Inspect ${target}` }
|
||||
}
|
||||
|
||||
/** Render an immutable Package source-inspection call. */
|
||||
/**
|
||||
* Render an immutable Package source-inspection call.
|
||||
* @param args - exact Plugin and Package identity.
|
||||
* @returns replay-safe generic call presentation.
|
||||
*/
|
||||
export function presentPackageInspectCall(args: { pluginId: string; packageId: string }): GenericCallView {
|
||||
return { card: 'generic', kind: 'read', title: `Inspect Cordis Package ${args.pluginId}/${args.packageId}` }
|
||||
}
|
||||
|
||||
/** Render a new or appended Package definition. */
|
||||
/**
|
||||
* Render a new or appended Package definition.
|
||||
* @param args - target Plugin, Package metadata, and source halves.
|
||||
* @returns replay-safe generic call presentation with source in raw input.
|
||||
*/
|
||||
export function presentDefineCall(args: {
|
||||
plugin: { kind: 'new'; idPrefix: string } | { kind: 'existing'; pluginId: string }
|
||||
name: string
|
||||
@@ -47,12 +70,20 @@ export function presentDefineCall(args: {
|
||||
}
|
||||
}
|
||||
|
||||
/** Render Plugin removal. */
|
||||
/**
|
||||
* Render Plugin removal.
|
||||
* @param args - Plugin identity to remove.
|
||||
* @returns replay-safe generic call presentation.
|
||||
*/
|
||||
export function presentUndefineCall(args: { pluginId: string }): GenericCallView {
|
||||
return { card: 'generic', kind: 'delete', title: `Remove dynamic Plugin ${args.pluginId}` }
|
||||
}
|
||||
|
||||
/** Render one exact Package activation. */
|
||||
/**
|
||||
* Render one exact Package activation.
|
||||
* @param args - Plugin, Package, and activation mode.
|
||||
* @returns replay-safe generic call presentation.
|
||||
*/
|
||||
export function presentRunCall(args: { pluginId: string; packageId: string; mode: 'run' | 'update' }): GenericCallView {
|
||||
return {
|
||||
card: 'generic',
|
||||
@@ -61,7 +92,11 @@ export function presentRunCall(args: { pluginId: string; packageId: string; mode
|
||||
}
|
||||
}
|
||||
|
||||
/** Render Plugin stop. */
|
||||
/**
|
||||
* Render Plugin stop.
|
||||
* @param args - Plugin identity to stop.
|
||||
* @returns replay-safe generic call presentation.
|
||||
*/
|
||||
export function presentStopCall(args: { pluginId: string }): GenericCallView {
|
||||
return { card: 'generic', kind: 'execute', title: `Stop dynamic Plugin ${args.pluginId}` }
|
||||
}
|
||||
@@ -18,7 +18,11 @@ const EVENT_OUTPUT = {
|
||||
} as const
|
||||
const HOST_EVENTS = EVENT_API.filter(event => !event.name.startsWith('cordis/'))
|
||||
|
||||
/** Construct Host providers over generated Catalogs, evaluator declarations, and live Tool scope. */
|
||||
/**
|
||||
* Construct Host providers over generated Catalogs, evaluator declarations, and live Tool scope.
|
||||
* @param ctx - Host context used for Agent-scoped live Tool queries.
|
||||
* @returns registrations for static catalogs and live Host capabilities.
|
||||
*/
|
||||
export function hostInspectProviders(ctx: Context): HostCordisInspectProviderRegistration[] {
|
||||
return [
|
||||
registration(
|
||||
|
||||
@@ -70,18 +70,15 @@
|
||||
"@deepseek-ai/dsh-cordis-client-runner": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-locale": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-runtime": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-test-runtime": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-sidebar": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slash": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-tool": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@testing-library/react": "^16.1.0",
|
||||
"@types/react": "~18.3.1",
|
||||
"@deepseek-ai/cordis": "workspace:^",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
|
||||
@@ -41,17 +41,17 @@ export function CordisRunRow({
|
||||
const loaded = useLoaded(snapshot => snapshot)
|
||||
const latest = useRunCards(snapshot => snapshot)
|
||||
const activeRuns = useActiveRuns(snapshot => snapshot)
|
||||
const successful = card.state === 'ok'
|
||||
const key = card.state === 'ok'
|
||||
&& card.pluginId !== null
|
||||
&& card.packageId !== null
|
||||
&& card.pluginRunId !== null
|
||||
&& card.seq !== null
|
||||
const key = successful ? cordisToolViewKey(card.pluginId!, card.packageId!) : null
|
||||
|
||||
? cordisToolViewKey(card.pluginId, card.packageId)
|
||||
: null
|
||||
useEffect(() => {
|
||||
if (!successful || key === null) return
|
||||
onObserveRunCard({ key, callId, seq: card.seq!, pluginRunId: card.pluginRunId! })
|
||||
}, [callId, card.pluginRunId, card.seq, key, onObserveRunCard, successful])
|
||||
if (key === null || card.seq === null || card.pluginRunId === null) return
|
||||
onObserveRunCard({ key, callId, seq: card.seq, pluginRunId: card.pluginRunId })
|
||||
}, [callId, card.pluginRunId, card.seq, key, onObserveRunCard])
|
||||
|
||||
const row = card.pluginId === null
|
||||
? undefined
|
||||
@@ -72,15 +72,15 @@ export function CordisRunRow({
|
||||
? 'superseded'
|
||||
: awaitingApproval
|
||||
? 'awaiting-approval'
|
||||
: attempt?.status === 'failed'
|
||||
? 'failed'
|
||||
: row !== undefined && card.packageId !== null
|
||||
? cordisVisibleStatus(row, card.packageId, loaded)
|
||||
: 'idle'
|
||||
: attempt?.status === 'failed'
|
||||
? 'failed'
|
||||
: row !== undefined && card.packageId !== null
|
||||
? cordisVisibleStatus(row, card.packageId, loaded)
|
||||
: 'idle'
|
||||
const status = t(READING_LABELS[reading])
|
||||
const summary = card.errorSummary
|
||||
?? (card.pluginId === null ? callId : `${card.pluginId}${card.packageId === null ? '' : ` · ${card.packageId}`}`)
|
||||
const showBusiness = successful && reading === 'running' && key !== null
|
||||
const showBusiness = reading === 'running' && key !== null
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -116,13 +116,13 @@ export function CordisRunRow({
|
||||
<div className={css.message}>{attempt.error.message}</div>
|
||||
)}
|
||||
{showBusiness && card.pluginId !== null && card.packageId !== null && card.pluginRunId !== null && (
|
||||
<div className={css.business} data-cordis-business-view={key ?? undefined}>
|
||||
<div className={css.business} data-cordis-business-view={key}>
|
||||
{renderSlot('tool.view.cordis', {
|
||||
pluginId: card.pluginId,
|
||||
packageId: card.packageId,
|
||||
pluginRunId: card.pluginRunId,
|
||||
}, {
|
||||
entryKey: key ?? undefined,
|
||||
entryKey: key,
|
||||
fallback: card.output === null ? null : <pre className={css.output}>{card.output}</pre>,
|
||||
})}
|
||||
</div>
|
||||
|
||||
@@ -79,7 +79,11 @@ function metaObject(block: Block): Record<string, unknown> | null {
|
||||
return block.meta as Record<string, unknown>
|
||||
}
|
||||
|
||||
/** Derive one Define card from its frozen call/result slice. */
|
||||
/**
|
||||
* Derive one Define card from its frozen call/result slice.
|
||||
* @param block - active or settled tool-call block.
|
||||
* @returns normalized Define card fields.
|
||||
*/
|
||||
export function cordisDefineCard(block: Block): CordisDefineCard {
|
||||
const settled = 'kind' in block
|
||||
const argsRaw = (settled ? block.call?.argsRaw : block.argsRaw) ?? ''
|
||||
@@ -102,7 +106,11 @@ export function cordisDefineCard(block: Block): CordisDefineCard {
|
||||
}
|
||||
}
|
||||
|
||||
/** Derive one Run card and its successful activation metadata. */
|
||||
/**
|
||||
* Derive one Run card and its successful activation metadata.
|
||||
* @param block - active or settled tool-call block.
|
||||
* @returns normalized Run card fields.
|
||||
*/
|
||||
export function cordisRunCard(block: Block): CordisRunCard {
|
||||
const settled = 'kind' in block
|
||||
const argsRaw = (settled ? block.call?.argsRaw : block.argsRaw) ?? ''
|
||||
|
||||
@@ -126,7 +126,7 @@ export function apply(ctx: ClientContext): void {
|
||||
const store = runCards.forSession(sessionId)
|
||||
return {
|
||||
hooks: { inventory, loaded, runCards: store, activeRuns: runner.activeRuns },
|
||||
onObserveRunCard: pointer => { store.observe(pointer) },
|
||||
onObserveRunCard: (pointer) => { store.observe(pointer) },
|
||||
}
|
||||
},
|
||||
}, CordisRunRow))
|
||||
@@ -139,7 +139,7 @@ export function apply(ctx: ClientContext): void {
|
||||
order: 1,
|
||||
candidates(session, { query }) {
|
||||
const rows = rowsOf(session.sessionId, query)
|
||||
return Promise.resolve(rows.map(row => {
|
||||
return Promise.resolve(rows.map((row) => {
|
||||
const packageId = row.nextPackageId ?? row.currentPackageId ?? row.packages.at(-1)?.packageId
|
||||
const pkg = packageId === undefined ? undefined : row.packages.find(candidate => candidate.packageId === packageId)
|
||||
return {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
export const NS = 'cordis'
|
||||
|
||||
/** Simplified Chinese Cordis UI messages. */
|
||||
export const zh = {
|
||||
'row.defineTitle': '注册 Cordis 插件',
|
||||
'row.runTitle': '运行 Cordis 插件',
|
||||
@@ -52,6 +53,7 @@ export const zh = {
|
||||
'body.copied': '已复制',
|
||||
} satisfies Record<string, string>
|
||||
|
||||
/** Translation keys owned by the Cordis UI namespace. */
|
||||
export type CordisKey = keyof typeof zh
|
||||
|
||||
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
||||
@@ -61,6 +63,7 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
||||
}
|
||||
}
|
||||
|
||||
/** English Cordis UI messages. */
|
||||
export const en = {
|
||||
'row.defineTitle': 'Define Cordis Plugin',
|
||||
'row.runTitle': 'Run Cordis Plugin',
|
||||
|
||||
@@ -47,7 +47,11 @@ function createStore(): CordisRunCardStore {
|
||||
export class CordisRunCardRegistry {
|
||||
private readonly sessions = new Map<SessionId, CordisRunCardStore>()
|
||||
|
||||
/** Return the persistent page-local Store for a session. */
|
||||
/**
|
||||
* Return the persistent page-local Store for a session.
|
||||
* @param sessionId - session whose cards share supersession state.
|
||||
* @returns the page-local Store retained for that session.
|
||||
*/
|
||||
forSession(sessionId: SessionId): CordisRunCardStore {
|
||||
let store = this.sessions.get(sessionId)
|
||||
if (store === undefined) {
|
||||
@@ -58,7 +62,12 @@ export class CordisRunCardRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
/** Build the Package business-view key shared by registrations and Run cards. */
|
||||
/**
|
||||
* Build the Package business-view key shared by registrations and Run cards.
|
||||
* @param pluginId - stable Plugin identity.
|
||||
* @param packageId - immutable Package identity.
|
||||
* @returns the shared business-view key.
|
||||
*/
|
||||
export function cordisToolViewKey(
|
||||
pluginId: CordisDynamicPluginId,
|
||||
packageId: CordisDynamicPackageId,
|
||||
|
||||
@@ -8,8 +8,16 @@ import type {
|
||||
/** The three product-visible lifecycle readings. */
|
||||
export type CordisVisibleStatus = 'idle' | 'client-pending' | 'running'
|
||||
|
||||
/** Locate one immutable Package inside a Plugin row. */
|
||||
export function packageOf(row: DynamicCordisInventoryRow, packageId: CordisDynamicPackageId) {
|
||||
/**
|
||||
* Locate one immutable Package inside a Plugin row.
|
||||
* @param row - owning Plugin inventory row.
|
||||
* @param packageId - immutable Package identity to locate.
|
||||
* @returns the matching Package metadata, or `undefined` when absent.
|
||||
*/
|
||||
export function packageOf(
|
||||
row: DynamicCordisInventoryRow,
|
||||
packageId: CordisDynamicPackageId,
|
||||
): DynamicCordisInventoryRow['packages'][number] | undefined {
|
||||
return row.packages.find(pkg => pkg.packageId === packageId)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user