feat: ship skill and MCP workflows with signed updater for v0.0.7

This commit is contained in:
xiaohan.yuan committed 2026-10-21 00:04:17 +08:00
1 parent 1b52df0603
commit a01085c656
136 files changed
+18646 -217

No files matched your search

+42
View File
@@ -50,10 +50,52 @@ export interface SkillRecord {
disableModelInvocation: boolean
source: 'imported' | 'discovered'
userModified: boolean
origin?: SkillInstallOrigin
error?: string
}
export interface SkillInstallOrigin {
recommendationId: string
repository: string
commit: string
packagePath: string
archiveSha256: string
version: string
installedAt: number
}
export interface RecommendedSkill {
id: string
name: string
description: string
author: string
license: string
repository: string
version: string
commit: string
packagePath: string
sha256: string
expectedBytes: number
containsScripts: boolean
avatarResource?: 'archify-owner'
}
export type SkillInstallPhase = 'downloading' | 'validating' | 'installing' | 'installed' | 'cancelled' | 'failed'
export interface SkillInstallProgress {
operationId: string
recommendationId: string
phase: SkillInstallPhase
receivedBytes: number
totalBytes?: number
error?: string
updatedAt: number
}
export interface RecommendedSkillInstallOptions { operationId: string; overwrite?: boolean }
export interface SkillDocument { skill: SkillRecord; content: string; files: string[] }
export interface SkillsApi {
recommendations(): Promise<RecommendedSkill[]>
installRecommended(id: string, options: RecommendedSkillInstallOptions): Promise<SkillRecord>
cancelInstall(operationId: string): Promise<{ cancelled: boolean }>
installState(): Promise<SkillInstallProgress | null>
onInstallProgress(listener: (progress: SkillInstallProgress) => void): () => void
onChanged(listener: () => void): () => void
list(cwd?: string): Promise<SkillRecord[]>
importPath(path?: string, overwrite?: boolean, kind?: 'directory' | 'zip'): Promise<SkillRecord | null>
read(id: string, cwd?: string): Promise<SkillDocument>
+1 -1
View File
@@ -42,7 +42,7 @@ export function normalizeContextUsageBreakdown(value: unknown): ContextUsageBrea
const item = value as Partial<ContextUsageBreakdown>
const categories = ['system','tools','conversation','mcp','skills','other'] as const
if (typeof item.requestId !== 'string' || !nonnegative(item.sequence) || !nonnegative(item.sampledAt)
|| !['model-tokenizer','o200k_base','utf8-estimate'].includes(String(item.method))
|| !['model-tokenizer','o200k_base','utf8-estimate','native-summary'].includes(String(item.method))
|| !['provider-request','prepared-request'].includes(String(item.scope)) || !item.counts
|| categories.some(key=>!nonnegative(item.counts?.[key])) || !nonnegative(item.inputTokens)) return undefined
const counts = Object.fromEntries(categories.map(key=>[key,item.counts![key]])) as ContextUsageBreakdown['counts']
+11
View File
@@ -33,6 +33,7 @@ import type { ExpertsApi, McpApi, SkillsApi } from './capabilities'
import type { ProjectInput, ProjectRecord } from './projects'
import type { AutomationApi } from './automation-api'
import type { LibraryApi } from './library'
import type { UpdatesApi } from './updates'
export interface ConfigPatchLike {
speechBaseUrl?: string
@@ -56,6 +57,12 @@ export interface EditInspection {
}
export interface SendPayload {
/** A durable queue-to-editor handoff, removed only after successful submission. */
queueEditId?: string
/** Display-only labels; the host validates capability IDs again before execution. */
capabilityLabels?: Record<string, string>
mcpServerIds?: string[]
command?: 'compact'
skillIds?: string[]
planId?: string
resources?: string[]
@@ -85,6 +92,8 @@ export interface SendResult {
}
export interface ActiveChatRun extends SendResult {
/** Authoritative history boundary when one native run advances to another input group. */
segmentBoundary?: boolean
sessionId: string
sequence: number
messages: ChatMessage[]
@@ -101,6 +110,7 @@ export interface WorkspaceAttachResult extends WorkspaceRegistryView {
/** preload 暴露给渲染进程的 API 形状 */
export interface YCodeApi {
updates: UpdatesApi
automation: AutomationApi
library: LibraryApi
projects: {
@@ -229,6 +239,7 @@ export interface YCodeApi {
openWindow(sessionId: string): Ipc<void>
}
chat: {
queue: import('./prompt-queue').PromptQueueApi
activeRun(sessionId: string): Ipc<ActiveChatRun | null>
onStarted(listener: (run: ActiveChatRun) => void): () => void
setSessionVisible(sessionId: string | null): Ipc<{ accepted: boolean }>
+28
View File
@@ -0,0 +1,28 @@
import type { SendPayload } from './ipc'
export interface QueuedPrompt {
id: string
createdAt: number
status: 'pending' | 'delivering' | 'unknown'
payload: SendPayload
error?: string
}
export interface PromptQueueSnapshot {
sessionId: string
revision: number
paused: boolean
reason?: string
items: QueuedPrompt[]
editingDraft?: QueuedPrompt
}
export interface PromptQueueApi {
get(sessionId: string): Promise<PromptQueueSnapshot>
enqueue(payload: SendPayload): Promise<PromptQueueSnapshot>
take(sessionId: string, id: string): Promise<QueuedPrompt>
restoreEdit(sessionId: string, id: string): Promise<PromptQueueSnapshot>
remove(sessionId: string, id: string): Promise<PromptQueueSnapshot>
reorder(sessionId: string, ids: string[]): Promise<PromptQueueSnapshot>
resume(sessionId: string): Promise<PromptQueueSnapshot>
sendNow(sessionId: string, id: string): Promise<PromptQueueSnapshot>
onChanged(listener: (snapshot: PromptQueueSnapshot) => void): () => void
}
+4 -1
View File
@@ -23,6 +23,8 @@ export interface ChatMessage {
/** Ordered inline presentation, validated against content and its actual payload collections. */
userContent?: UserContentBlock[]
skills?: Array<{ id: string; name: string; uri: string }>
mcpServerIds?: string[]
command?: 'compact'
assistantDisplay?: import('./capabilities').ExpertBinding
/** The model selected when this assistant turn started; kept with history. */
modelId?: string
@@ -258,7 +260,7 @@ export interface ContextUsageBreakdown {
sampledAt: number
modelId?: string
contextWindow?: number
method: 'model-tokenizer' | 'o200k_base' | 'utf8-estimate'
method: 'model-tokenizer' | 'o200k_base' | 'utf8-estimate' | 'native-summary'
scope: 'provider-request' | 'prepared-request'
counts: Record<ContextUsageCategory, number>
inputTokens: number
@@ -292,6 +294,7 @@ export interface GitBranchInfo {
/** 一次会话的元信息 */
export interface SessionMeta {
permissionMode?: import('./conversation').PermissionMode
projectId?: string
id: string
title: string
+25
View File
@@ -0,0 +1,25 @@
export type UpdateStatus = 'idle' | 'checking' | 'up-to-date' | 'available' | 'downloading' | 'ready' | 'installing' | 'error' | 'unsupported'
export interface UpdateState {
status: UpdateStatus
currentVersion: string
version?: string
notes?: string
progress?: number
error?: string
lastChecked?: string
dismissedVersion?: string
unfinishedVersion?: string
lastExplicit?: boolean
}
export interface UpdatesApi {
getState(): Promise<UpdateState>
check(): Promise<UpdateState>
download(): Promise<UpdateState>
install(): Promise<UpdateState>
dismiss(): Promise<UpdateState>
openDownloadPage(): Promise<void>
openLog(): Promise<void>
onState(listener: (state: UpdateState) => void): () => void
}
+1
View File
@@ -15,6 +15,7 @@ export interface WorkspaceRegistryView {
export type ThinkingLevel = 'off' | 'auto' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'
export interface SessionContext {
permissionMode?: import('./conversation').PermissionMode
projectId?: string
workspaceId?: string
cwd?: string