merge: sync master into desktop welcome branch

This commit is contained in:
lsdsjy committed 2026-09-21 22:11:39 +08:00
commit 0d985d55a2
94 files changed
+1751 -353

No files matched your search

@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-09-21-creator-skills-progressive-disclosure.md
2026-09-21-creator-skills-progressive-disclosure.md: bdd70beda7c92584a336cda856d196ac23d1f968
2026-09-21-creator-skills-progressive-disclosure.zh.md: 265d9db21a5288b4d589078ff85a75eb74f3b237
@@ -0,0 +1,25 @@
# Agent Note: Creator skills disclose progressively and may read sources
Status: implemented
English | [中文](2026-09-21-creator-skills-progressive-disclosure.zh.md)
## Problem
The shipped `cordis-plugin-development` skill carried three mutually exclusive recipes and a verification checklist in one 8.7 KB file, above the 8192-character threshold at which the standard preset's tool-result pruner keeps only the head and tail. Both creator skills forbade reading DSH package sources, which made the skill text the only knowledge source and left the agent guessing whenever it did not cover a case. The skills also addressed the profile as `$DSH_HOME/profiles/<profile>`, a name the agent's shell never received.
## Decision
`SKILL.md` keeps the procedure, the knowledge-source order, and an index table; recipes move to `references/` files and copyable bundles to `templates/`, all resolved through the skill's base directory that the `skill` tool already reports. A test keeps the leading file under the pruner head and checks that every referenced file exists and every template parses.
Knowledge sources are ordered, never forbidden: inspection through `cordis_inspect_query`, the package README under the `packageDir` that `Config.listConfigs` resolves through the profile package lookup, then built `lib/` declarations or checkout sources when a question remains. Bundled packages resolve from the dsh installation and profile-installed ones from the profile, so skills never derive a package path from the profile directory. The shell registry exposes `DSH_PROFILE` and `DSH_PROFILE_DIR` as reserved built-ins whenever the launcher provided a profile context.
A third skill, `cordis-composition-reference`, holds the Loader patch dialect and a generated list of loadable plugin packages, freshness-gated in `doc-sync`, so procedure skills stop growing.
## Alternatives considered
Adding creator-mode text to the system prompt is paid every turn; the skill catalog already routes on demand. Shipping `docs/` to the agent would dilute maintainer material into a runtime context that needs installed-profile facts. Keeping the source-reading ban would have required the skills to restate every declaration they might need.
## Consequences
The first skill load costs about 3.9 KB, and each recipe 1 to 3 KB more. Recorded sessions that load `editing-cordis-compositions` refresh when its text changes. Compositions booted without a profile omit the two profile variables, and the skills say so.
@@ -0,0 +1,25 @@
# Agent Note: 创造模式 skill 渐进式披露并允许读源码
Status: implemented
[English](2026-09-21-creator-skills-progressive-disclosure.md) | 中文
## Problem
出货的 `cordis-plugin-development` skill 把三个互斥的配方和一份验证清单放在一个 8.7 KB 的文件里,超过了标准 preset 工具结果修剪器只保留头尾的 8192 字符阈值。两个创造模式 skill 都禁止读取 DSH 包源码,使 skill 文本成为唯一知识来源,未覆盖的情况只能靠猜。skill 还用 `$DSH_HOME/profiles/<profile>` 指代 profile,而 agent 的 shell 从未拿到这个名字。
## Decision
`SKILL.md` 只保留流程、知识来源顺序和索引表;配方移入 `references/`,可复制的组合包移入 `templates/`,都通过 `skill` 工具已报告的 skill 基目录解析。一个测试保证开头文件在修剪器头部之内,并检查每个被引用的文件存在、每个模板可解析。
知识来源有顺序而不禁止:先通过 `cordis_inspect_query` 检查,再读 `Config.listConfigs` 经 profile 包查找解析出的 `packageDir` 下的包 README,仍有疑问时读构建后的 `lib/` 声明或 checkout 源码。自带包从 dsh 安装目录解析,profile 自行安装的包从 profile 解析,所以 skill 不再从 profile 目录推导包路径。启动器提供 profile 上下文时,shell 注册表把 `DSH_PROFILE` 与 `DSH_PROFILE_DIR` 作为保留内置变量暴露。
第三个 skill `cordis-composition-reference` 承载 Loader patch 方言和生成的可加载插件包清单,并在 `doc-sync` 中做新鲜度门禁,让流程 skill 不再增长。
## Alternatives considered
在系统提示里加创造模式文本每轮都要付费;skill 目录已能按需路由。把 `docs/` 出货给 agent 会把维护者材料稀释进需要安装态 profile 事实的运行时上下文。保留禁读源码则要求 skill 复述它可能需要的每一条声明。
## Consequences
首次加载 skill 约 3.9 KB,每个配方再加 1 到 3 KB。加载 `editing-cordis-compositions` 的录制会话在其文本变化时需要刷新。不带 profile 启动的组合没有这两个 profile 变量,skill 对此有说明。
@@ -0,0 +1,6 @@
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-09-21-live-config-inspect-provider.md
2026-09-21-live-config-inspect-provider.md: b4be50852693665780da37b03c130339afa0ef57
2026-09-21-live-config-inspect-provider.zh.md: e1eead7b68f12be320e5c34b621178f44cd26282
@@ -0,0 +1,25 @@
# Agent Note: Live plugin Config inspection replaces the Host Builtin provider
Status: implemented
English | [中文](2026-09-21-live-config-inspect-provider.zh.md)
## Problem
Creator mode agents write `config:` rows for installed plugins without any runtime source of Config schemas. The CLI `--dump-config-schema` reference is not reachable from the running session, and the `Builtin` Host inspect provider advertised sandbox symbols for a dynamic Host half the model can no longer define.
## Decision
`@deepseek-ai/dsh-tool-cordis/host` registers a `Config` inspect provider and no `Builtin` provider. `Config.listConfigs` reads the live Loader tree: without input it returns every entry id, plugin name, and Config status; with an entry id it projects that entry's native Schemastery Config through the app-boot projector into one self-contained JSON Schema document. Inactive entries report `inactive` without a schema.
The provider walks `ctx.loader.entries()` and each fiber's runtime Config instead of running the boot-free collector, because a running profile already holds the module-resolution interception that the collector installs and forbids overlapping. App-boot exports the projector, the native-schema check, and the shared `loaderExpression` definition for this use; the CLI dump keeps its profile-composition semantics.
## Alternatives considered
**Re-run `generateConfigSchema` inside the Host.** It would re-import every plugin module under a second interception, which the collector forbids while one is installed, and it describes the profile files rather than the mounted tree.
**Teach the shipped skill to run the CLI dump.** It requires the profile name in the agent's shell and a `dsh` executable on its PATH, neither of which the session guarantees, and its output is unbounded.
## Consequences
Agents can read a mounted plugin's Config schema before authoring a patch. Runtime-created Agent preset trees stay outside the Loader and are not listed. The Client `Builtin` provider is unchanged: Client plugin code still runs in the browser module loader it describes.
@@ -0,0 +1,25 @@
# Agent Note: 运行中插件 Config 检查取代 Host Builtin provider
Status: implemented
[English](2026-09-21-live-config-inspect-provider.md) | 中文
## Problem
创造模式的 agent 为已安装插件编写 `config:` 行时,没有任何运行时的 Config schema 来源。CLI 的 `--dump-config-schema` 参考在运行中的会话里不可达,而 `Builtin` Host 检查 provider 宣告的是模型已无法定义的动态 Host half 的沙箱符号。
## Decision
`@deepseek-ai/dsh-tool-cordis/host` 注册 `Config` 检查 provider,不再注册 `Builtin` provider。`Config.listConfigs` 读取运行中的 Loader 树:无输入时返回每个 entry 的 id、插件名和 Config 状态;给定 entry id 时,通过 app-boot 的投影器把该 entry 的原生 Schemastery Config 投影为一份自包含的 JSON Schema 文档。未激活的 entry 报告 `inactive` 且不带 schema。
provider 遍历 `ctx.loader.entries()` 和每个 fiber 的运行时 Config,而不是运行免启动的收集器,因为运行中的 profile 已持有收集器要安装且禁止重叠的模块解析拦截。app-boot 为此导出投影器、原生 schema 判定和共享的 `loaderExpression` 定义;CLI dump 保留其 profile 组合语义。
## Alternatives considered
**在 Host 内重跑 `generateConfigSchema`。** 它会在第二层拦截下重新导入每个插件模块,而收集器禁止在已安装拦截时重叠,并且它描述的是 profile 文件而非已挂载的树。
**让出货的 skill 去运行 CLI dump。** 它需要 agent 的 shell 里有 profile 名和 PATH 上的 `dsh` 可执行文件,会话都不保证,而且输出无界。
## Consequences
agent 可以在编写 patch 前读取已挂载插件的 Config schema。运行时生成的 Agent preset 树仍在 Loader 之外,不会被列出。Client 的 `Builtin` provider 不变:Client 插件代码仍运行在它所描述的浏览器模块加载器中。
+39 -1
View File
@@ -668,7 +668,7 @@ describe('a user preset declared from the shipped cordis rows', () => {
expect(listed.isError).toBe(false)
const providers = (JSON.parse(resultText(listed)) as { providers: Array<{ id: string; platform: string }> }).providers
expect(providers.filter(provider => provider.platform === 'host').map(provider => provider.id))
.toEqual(['Service', 'Event', 'Builtin', 'Tool'])
.toEqual(['Service', 'Event', 'Config', 'Tool'])
// The `Tool` provider is the one built over the host context: it
// answers with the shared registry's view of the REQUESTING agent,
@@ -685,6 +685,44 @@ describe('a user preset declared from the shipped cordis rows', () => {
expect(tools.map(tool => tool.name)).toEqual(expect.arrayContaining([
'bash', 'cordis_inspect_list', 'cordis_inspect_query', 'plugin_manager',
]))
// The `Config` provider reads the booted profile tree: the shipped `tools` row declares a Config,
// and the bootstrap include row is a carrier. The name filter keeps each page small.
type ConfigRow = { id: string; patchId: string; name: string; status: string }
type ConfigPage = { data: { entries: ConfigRow[]; total: number; nextOffset: number | null } }
const listConfigs = async (input: Record<string, string | number>, callId: string): Promise<ConfigPage['data']> => {
const page = await copyCtx.tools.execute({
callId: ToolCallId(callId),
name: 'cordis_inspect_query',
arguments: { platform: 'host', provider: 'Config', method: 'listConfigs', input },
signal,
agent: copied.agent,
})
expect(page.isError, resultText(page)).toBe(false)
return (JSON.parse(resultText(page)) as ConfigPage).data
}
const toolsRows = await listConfigs({ name: '@deepseek-ai/dsh-tools' }, 'copied-preset-inspect-configs')
const toolsRow = toolsRows.entries[0]
expect(toolsRow).toMatchObject({ patchId: 'tools', status: 'schema' })
expect(toolsRows).toMatchObject({ total: 1, nextOffset: null })
const includes = await listConfigs({ name: 'cordis:include' }, 'copied-preset-inspect-includes')
expect(includes.entries.find(entry => entry.patchId === 'include')).toMatchObject({ status: 'tree' })
const firstPage = await listConfigs({ limit: 5 }, 'copied-preset-inspect-page')
expect(firstPage.entries).toHaveLength(5)
expect(firstPage.nextOffset).toBe(5)
const projected = await copyCtx.tools.execute({
callId: ToolCallId('copied-preset-inspect-config'),
name: 'cordis_inspect_query',
arguments: { platform: 'host', provider: 'Config', method: 'listConfigs', input: { entry: toolsRow!.id } },
signal,
agent: copied.agent,
})
expect(projected.isError).toBe(false)
type Projected = { status: string; acceptsMissing: unknown; schema: { $defs: Record<string, unknown> } }
const { data } = JSON.parse(resultText(projected)) as { data: Projected }
expect(data.status).toBe('schema')
expect(data.schema.$defs).toHaveProperty('loaderExpression')
expect(JSON.stringify(data.schema)).toContain('"mode"')
} finally {
await copied.dispose()
await shipped.dispose()
+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write apps/desktop/README.md
README.md: 71a35816eee5f1c2092797bfe7562e8a38c80b86
README.zh.md: ce6363d06841f0c24525f1cca9c750a0e95b5d46
README.md: 711e65fb5feaea601800218801d138a0df9b3f97
README.zh.md: 183d195a9af485c9ffe5a3e5bf65e3a459582b93
+2
View File
@@ -16,6 +16,8 @@ Desktop Host Platform API requests use the same `x-client-platform` mapping as u
Desktop microphone access is restricted to audio requests from the primary `dsh-app://app` frame. macOS uses system microphone authorization and a packaged usage description.
Press F12 (Fn+F12 on media-key keyboards), Command+Option+I on macOS, or Ctrl+Shift+I on Windows to toggle DevTools for the focused application page, including in packaged builds. These native shortcuts use hidden application-menu items. Update overlays and packaged embedded browser guests disable DevTools.
## Key technical decisions
The original artwork lives in `resources/icon.png` and `resources/icon.svg`; platform adaptations retain the whale and gradients in `resources/icon-windows.*` and `resources/icon-macos.*`. Export each platform SVG as a transparent 1024×1024 PNG. Electron-builder generates the multi-size ICO for the Windows application, installer, and uninstaller ([Windows icon requirements](https://learn.microsoft.com/en-us/windows/apps/design/iconography/app-icon-construction)). The installation pages use matching artwork in both themes; the uninstaller's welcome and finish pages share `installer/assets/uninstaller-sidebar.png`, converted to a 164×314 BMP during preparation.
+2
View File
@@ -16,6 +16,8 @@ Desktop Host 的 Platform API 请求与更新策略请求使用相同的 `x-clie
桌面麦克风访问仅允许主 `dsh-app://app` 页面发起的音频请求。macOS 使用系统麦克风授权与随包用途说明。
按 F12(多媒体功能键键盘上为 Fn+F12)、macOS 的 Command+Option+I 或 Windows 的 Ctrl+Shift+I,可切换当前获得焦点的应用页面的 DevTools,打包版本同样支持。这些原生快捷键通过隐藏的应用菜单项注册。更新遮罩和打包版本的内嵌浏览器禁用 DevTools。
## 关键技术决策
设计师原稿位于 `resources/icon.png` 和 `resources/icon.svg`;平台适配保留鲸鱼与渐变,分别位于 `resources/icon-windows.*` 和 `resources/icon-macos.*`。将各平台 SVG 导出为透明的 1024×1024 PNG。electron-builder 为 Windows 应用、安装程序和卸载程序生成多尺寸 ICO([Windows 图标要求](https://learn.microsoft.com/en-us/windows/apps/design/iconography/app-icon-construction))。安装页面在两种主题下使用匹配的图案;卸载程序的欢迎和完成页共用 `installer/assets/uninstaller-sidebar.png`,准备阶段将其转换为 164×314 BMP。
+8 -3
View File
@@ -157,6 +157,7 @@ function createWindow(preload: string, show = false, primary = false): BrowserWi
sandbox: true,
webSecurity: true,
webviewTag: primary,
devTools: true,
},
})
window.webContents.setWindowOpenHandler(({ url }) => {
@@ -769,10 +770,14 @@ async function main(): Promise<void> {
{ role: 'quit', ...(darwin ? { label: currentDesktopLocale().messages.quitApplication }
: process.platform === 'win32' ? { label: currentDesktopLocale().messages.exitApplication } : {}) },
]
const devToolsItems: MenuItemConstructorOptions[] = [
{ role: 'toggleDevTools', visible: false },
{ role: 'toggleDevTools', visible: false, accelerator: 'F12' },
]
const installMenu = (): void => {
Menu.setApplicationMenu(process.platform === 'win32' ? null : Menu.buildFromTemplate([{
label: process.platform === 'darwin' ? app.name : currentDesktopLocale().messages.application,
submenu: applicationItems(),
Menu.setApplicationMenu(Menu.buildFromTemplate(process.platform === 'win32' ? devToolsItems : [{
label: darwin ? app.name : currentDesktopLocale().messages.application,
submenu: [...applicationItems(), ...devToolsItems],
}, ...platformMenus]))
}
installMenu()
+16 -3
View File
@@ -390,6 +390,16 @@ describe('desktop main startup', () => {
expect((await handler(new Request('dsh-app://unknown/update-dialog.html'))).status).toBe(404)
})
it('installs hidden native DevTools shortcuts in the macOS application menu', async () => {
vi.stubGlobal('process', { ...process, platform: 'darwin' })
await readyForUpdate()
expect(applicationMenuItems().slice(-2)).toEqual([
{ role: 'toggleDevTools', visible: false },
{ role: 'toggleDevTools', visible: false, accelerator: 'F12' },
])
expect(harness.windows[0]!.options).toMatchObject({ webPreferences: { devTools: true } })
})
it.each([
['darwin', true, 'en-US'],
['darwin', false, 'zh-CN'],
@@ -581,7 +591,10 @@ describe('desktop main startup', () => {
} else if (platform === 'win32') {
expect(window.options).toMatchObject({ titleBarStyle: 'hidden', titleBarOverlay: { height: WINDOWS_TITLEBAR_HEIGHT } })
expect(window.options).not.toHaveProperty('vibrancy')
expect(harness.menu.setApplicationMenu).toHaveBeenCalledWith(null)
expect(harness.menu.mock.calls[0]![0]).toEqual([
{ role: 'toggleDevTools', visible: false },
{ role: 'toggleDevTools', visible: false, accelerator: 'F12' },
])
} else {
expect(window.options).not.toHaveProperty('titleBarStyle')
expect(window.options).not.toHaveProperty('vibrancy')
@@ -701,7 +714,7 @@ describe('desktop main startup', () => {
window.webContents.mainFrame.url = 'dsh-app://unowned/index.html'
listener(event, 'zh-CN', '#fff', '#000')
expect(window.setTitleBarOverlay).not.toHaveBeenCalled()
expect(harness.menu.setApplicationMenu).toHaveBeenCalledExactlyOnceWith(null)
expect(harness.menu.setApplicationMenu).toHaveBeenCalledOnce()
})
it('maps Windows caption menus to localized native commands and rejects foreign popup requests', async () => {
@@ -755,7 +768,7 @@ describe('desktop main startup', () => {
? ['Desktop test', 'fileMenu', 'editMenu', 'windowMenu']
: ['Application', 'editMenu'])
const application = template[0]!.submenu as MenuItemConstructorOptions[]
expect(application.map(describeItem)).toEqual(platform === 'darwin'
expect(application.filter(item => item.visible !== false).map(describeItem)).toEqual(platform === 'darwin'
? ['about', 'separator', en.checkUpdatesMenu, 'separator', 'hide', 'hideOthers', 'unhide', 'separator', 'quit']
: ['about', 'separator', en.checkUpdatesMenu, 'separator', 'quit'])
expect(harness.menu.setApplicationMenu).toHaveBeenCalledOnce()
+32 -14
View File
@@ -1,11 +1,11 @@
/** The preset settings page only views and selects; authoring is guided to Creator mode. */
/** The preset settings page only selects a preset; creating one starts a Creator-mode task. */
import { fileURLToPath } from 'node:url'
import { join } from 'node:path'
import type { Browser, Page } from 'playwright'
import { chromium } from 'playwright'
import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
import { captureStableAria, compareOrRefreshGolden, launchWebScaffold, watchConsole, webSnapshotMode, type WebScaffold } from './scaffold.ts'
import { openSettingsFromAccountMenu, ZH_BROWSER_LOCALE, saveFailureShot } from './support.ts'
import { openSettingsFromAccountMenu, ZH_BROWSER_LOCALE, connectFreshWorkspaceZh, saveFailureShot } from './support.ts'
const EXPECTED = fileURLToPath(new URL('./expected/agent-preset-authoring', import.meta.url))
const mode = webSnapshotMode()
@@ -32,8 +32,9 @@ describe('web e2e: preset roster guidance', () => {
await expect.poll(() => page.locator('[data-agent-preset-id]').count()).toBe(4)
const snapshot = await captureStableAria(page, '[role="dialog"]', scaffold.workspaceCwd)
await compareOrRefreshGolden(join(EXPECTED, 'section.expected.md'), snapshot, mode)
expect(snapshot).toContain('如何创建或修改预设')
expect(snapshot).not.toContain('新建预设')
expect(snapshot).toContain('让 Agent 帮我创建预设模式')
expect(snapshot).not.toContain('查看配置')
expect(snapshot).not.toContain('复制预设')
expect(snapshot).not.toContain('编辑插件')
expect(snapshot).not.toContain('打开目录')
expect(snapshot).not.toContain('删除')
@@ -54,16 +55,33 @@ describe('web e2e: preset roster guidance', () => {
expect(await settings.getByRole('button', { name: '新任务默认: 标准模式', exact: true }).getAttribute('aria-pressed')).toBe('true')
})
it('guides authoring to Creator mode instead of editing here', async () => {
onTestFailed(() => saveFailureShot(page, 'preset-roster-authoring-help'))
await page.getByRole('button', { name: '如何创建或修改预设', exact: true }).click()
const help = page.getByRole('dialog', { name: '创建和修改预设', exact: true })
await help.waitFor()
const snapshot = await captureStableAria(page, '[role="dialog"][aria-label="创建和修改预设"]', scaffold.workspaceCwd)
await compareOrRefreshGolden(join(EXPECTED, 'authoring-help.expected.md'), snapshot, mode)
expect(snapshot).toContain('让 Agent 帮我创建预设模式')
await help.getByRole('button', { name: '关闭', exact: true }).last().click()
await help.waitFor({ state: 'detached' })
it('starts a Creator-mode task from the section entry', async () => {
onTestFailed(() => saveFailureShot(page, 'preset-roster-creator'))
// The entry stages the self-referential preset and lands a new task on it,
// so the flow needs a connected workspace to enter.
await page.getByRole('dialog', { name: '设置' }).getByRole('button', { name: '关闭', exact: true }).last().click()
await connectFreshWorkspaceZh(page, scaffold.workspaceCwd)
await openSettingsFromAccountMenu(page, 'zh')
const settings = page.getByRole('dialog', { name: '设置' })
await settings.getByRole('button', { name: 'Agent 预设' }).click()
await settings.getByRole('button', { name: '让 Agent 帮我创建预设模式', exact: true }).click()
await settings.waitFor({ state: 'detached', timeout: 10_000 })
await expect.poll(async () => {
const response = await scaffold.hostFetch('/api/session/list', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
type: 'client-request', rpcId: 'creator-draft-stage', method: 'session/list',
payload: { args: { _request: {} } },
}),
})
const body = await response.json() as {
result: { value?: { items: { projections?: { values: { agentPreset?: string | null } } }[] } }
}
return body.result.value?.items
.map(item => item.projections?.values.agentPreset)
.filter(preset => typeof preset === 'string') ?? []
}, { timeout: 15_000 }).toContain('cordis')
})
it('runs without page errors or model calls', () => { expect(tripwire.pageErrors).toEqual([]) })
@@ -1,6 +0,0 @@
- dialog "创建和修改预设":
- heading "创建和修改预设" [level=2]
- button "关闭"
- paragraph: 预设由已安装的 bundle 声明,本页只负责选择。要创建或修改预设,请用「创造模式」开始一个任务:Agent 会编写一个声明或覆盖该预设的 bundle,并安装到当前 profile。
- button "关闭"
- button "让 Agent 帮我创建预设模式"
@@ -8,7 +8,6 @@
- button "打开配置文件"
- button "关闭"
- heading "Agent 预设" [level=2]
- button "如何创建或修改预设"
- paragraph: 选择 Agent 的工具和工作方式。日常任务用「标准模式」,扩展 DSH 的能力用「创造模式」。
- text: 新任务可选择模式 Beta
- paragraph: 开启后,可为每个新任务选择模式,并在这里设置默认值。关闭后,新任务使用应用配置的默认预设。已有任务不受影响。
@@ -43,3 +42,5 @@
- text: 用对话定制 DSH:让 Agent 编写插件,添加新功能或界面;也能组合工具和提示词,创建自己的模式。
- 'button "模式说明: 创造模式"': 模式说明
- 'button "如何使用: 创造模式"': 如何使用
- heading "自定义" [level=3]
- button "让 Agent 帮我创建预设模式"
+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/config-catalog.md
config-catalog.md: ccdfe0755073fbfd859a75c0f446538dd416223d
config-catalog.zh.md: 6c9d038a540f316191fe59c67ac5790bc231e9ac
config-catalog.md: 573d16f37b9ce3c123c55c366d8ee6424feb3d64
config-catalog.zh.md: 9d827a4c65ec198239d7d1ef29735747a39838b9
+2 -2
View File
@@ -646,7 +646,7 @@ export interface Config {
}
```
Source: [`packages/extensions/cordis-host-runner/src/index.ts:94`](../packages/extensions/cordis-host-runner/src/index.ts)
Source: [`packages/extensions/cordis-host-runner/src/index.ts:93`](../packages/extensions/cordis-host-runner/src/index.ts)
<a id="deepseek-aidsh-credentials-local"></a>
@@ -2675,7 +2675,7 @@ export interface Config {
}
```
Source: [`packages/shell/shell-env/src/index.ts:28`](../packages/shell/shell-env/src/index.ts)
Source: [`packages/shell/shell-env/src/index.ts:30`](../packages/shell/shell-env/src/index.ts)
<a id="deepseek-aidsh-skill"></a>
+2 -2
View File
@@ -648,7 +648,7 @@ export interface Config {
}
```
来源: [`packages/extensions/cordis-host-runner/src/index.ts:94`](../packages/extensions/cordis-host-runner/src/index.ts)
来源: [`packages/extensions/cordis-host-runner/src/index.ts:93`](../packages/extensions/cordis-host-runner/src/index.ts)
<a id="deepseek-aidsh-credentials-local"></a>
@@ -2677,7 +2677,7 @@ export interface Config {
}
```
来源: [`packages/shell/shell-env/src/index.ts:28`](../packages/shell/shell-env/src/index.ts)
来源: [`packages/shell/shell-env/src/index.ts:30`](../packages/shell/shell-env/src/index.ts)
<a id="deepseek-aidsh-skill"></a>
+1 -1
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/event-producer-consumer.md
event-producer-consumer.md: 1754d693aeb56f4e1a4097c3c3ac7fe5aa3ce2fc
event-producer-consumer.md: 13ac82b2c9fb0b2aeeaa2bec9c736db5b5b63992
event-producer-consumer.zh.md: bfd2ca7dbcd1b874b34ad2c45eceeaf84ffa70ec
+1 -1
View File
@@ -26,7 +26,7 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| `api-session/error` | `emit` | [`packages/api/session-controller/src/types.ts:610`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
| `api-session/removed` | `emit` | [`packages/api/session-controller/src/types.ts:589`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
| `api-session/status` | `emit` | [`packages/api/session-controller/src/types.ts:596`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
| `app-boot/config-reload` | `emit` | [`packages/boot/app-boot/src/index.ts:43`](../packages/boot/app-boot/src/index.ts) | [`app-boot`](../packages/boot/app-boot) (`emit`) | [`settings`](../packages/settings/settings) |
| `app-boot/config-reload` | `emit` | [`packages/boot/app-boot/src/index.ts:45`](../packages/boot/app-boot/src/index.ts) | [`app-boot`](../packages/boot/app-boot) (`emit`) | [`settings`](../packages/settings/settings) |
| `approval/request` | `waterfall` | [`packages/interaction/user-approval/src/types.ts:85`](../packages/interaction/user-approval/src/types.ts) | [`user-approval`](../packages/interaction/user-approval) (`waterfall`) | [`acp`](../packages/acp/acp), `remotes` |
| `authorization/settled` | `emit` | [`packages/credentials/authorization/src/index.ts:57`](../packages/credentials/authorization/src/index.ts) | [`authorization`](../packages/credentials/authorization) (`events.dispatch`) | [`authorization`](../packages/credentials/authorization) |
| `commands/change` | `emit` | [`packages/interaction/commands/src/types.ts:89`](../packages/interaction/commands/src/types.ts) | [`commands`](../packages/interaction/commands) (`events.dispatch`) | `remotes` |
+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/module-graph.md
module-graph.md: 8b277a4735da4a86ad32060e46ea67810a50dc7d
module-graph.zh.md: 4183ba53d650cb41c70215eeef65a05aaf02a4f6
module-graph.md: 868a51d96e818b6e815defd6acce758ab7251d0b
module-graph.zh.md: aa8edca41eeff8d8a05c03e3310e09ed7b3eaabd
+4 -2
View File
@@ -928,6 +928,7 @@ flowchart TD
pkg_session_title_first_prompt_llm --> pkg_session
pkg_session_title_first_prompt_llm --> pkg_session_title
pkg_session_title_first_prompt_llm --> pkg_session_title_llm
pkg_shell_env --> pkg_app_boot
pkg_shell_env --> pkg_home_paths
pkg_shell_env --> pkg_shell
pkg_shell_env --> pkg_tools
@@ -1001,6 +1002,7 @@ flowchart TD
pkg_experimental_auto_review --> pkg_session
pkg_experimental_auto_review --> pkg_tools
pkg_tool_cordis --> pkg_agent
pkg_tool_cordis --> pkg_app_boot
pkg_tool_cordis --> pkg_cordis_host_runner
pkg_tool_cordis --> pkg_tools
pkg_host_plugin_inventory --> pkg_agent_preset_registry
@@ -1575,7 +1577,7 @@ flowchart TD
| [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy) | `session` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`tools`](../packages/core/tools) |
| [`session-title-all-prompts-llm`](../packages/session/session-title-all-prompts-llm) | `session` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`session-title-llm`](../packages/session/session-title-llm) |
| [`session-title-first-prompt-llm`](../packages/session/session-title-first-prompt-llm) | `session` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`session-title-llm`](../packages/session/session-title-llm) |
| [`shell-env`](../packages/shell/shell-env) | `shell` | [`home-paths`](../packages/util/home-paths), [`shell`](../packages/shell/shell), [`tools`](../packages/core/tools) |
| [`shell-env`](../packages/shell/shell-env) | `shell` | [`app-boot`](../packages/boot/app-boot), [`home-paths`](../packages/util/home-paths), [`shell`](../packages/shell/shell), [`tools`](../packages/core/tools) |
| [`tool-bash-persistent`](../packages/shell/tool-bash-persistent) | `shell` | [`agent`](../packages/core/agent), [`terminal`](../packages/terminal/terminal), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`tool-pwsh-persistent`](../packages/shell/tool-pwsh-persistent) | `shell` | [`agent`](../packages/core/agent), [`terminal`](../packages/terminal/terminal), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`ssh`](../packages/ssh/ssh) | `ssh` | [`brand`](../packages/util/brand), [`fs`](../packages/fs/fs), [`fs-local`](../packages/fs/fs-local), [`fs-sandbox`](../packages/fs/fs-sandbox), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-local`](../packages/sandbox/sandbox-local), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session-projection`](../packages/session/session-projection), [`subprocess`](../packages/subprocess/subprocess), [`subprocess-local`](../packages/subprocess/subprocess-local) |
@@ -1588,7 +1590,7 @@ flowchart TD
| [`api-settings-controller`](../packages/api/settings-controller) | `api` | [`agent-preset-registry`](../packages/preset/agent-preset-registry), [`credentials`](../packages/credentials/credentials), [`native-command`](../packages/util/native-command), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`typert-protocol`](../packages/typert/protocol) |
| [`web-app`](../packages/bundle/web-app) | `bundle` | [`shell-env`](../packages/shell/shell-env), [`system-prompt`](../packages/core/system-prompt) |
| [`experimental-auto-review`](../packages/experimental/auto-review) | `experimental` | [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), [`llm`](../packages/llm/llm), [`permission-presets`](../packages/interaction/permission-presets), [`session`](../packages/core/session), [`tools`](../packages/core/tools) |
| [`tool-cordis`](../packages/extensions/tool-cordis) | `extensions` | [`agent`](../packages/core/agent), [`cordis-host-runner`](../packages/extensions/cordis-host-runner), [`tools`](../packages/core/tools) |
| [`tool-cordis`](../packages/extensions/tool-cordis) | `extensions` | [`agent`](../packages/core/agent), [`app-boot`](../packages/boot/app-boot), [`cordis-host-runner`](../packages/extensions/cordis-host-runner), [`tools`](../packages/core/tools) |
| [`host-plugin-inventory`](../packages/host/plugin-inventory) | `host` | [`agent-preset-registry`](../packages/preset/agent-preset-registry), [`brand`](../packages/util/brand), [`typert-protocol`](../packages/typert/protocol) |
| [`mcp-client`](../packages/mcp/mcp-client) | `mcp` | [`attachment`](../packages/attachment/attachment), [`llm`](../packages/llm/llm), [`mcp-resources`](../packages/mcp/mcp-resources), [`scope`](../packages/core/scope), [`subprocess`](../packages/subprocess/subprocess), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`agent-preset`](../packages/preset/agent-preset) | `preset` | [`agent-preset-registry`](../packages/preset/agent-preset-registry) |
+4 -2
View File
@@ -930,6 +930,7 @@ flowchart TD
pkg_session_title_first_prompt_llm --> pkg_session
pkg_session_title_first_prompt_llm --> pkg_session_title
pkg_session_title_first_prompt_llm --> pkg_session_title_llm
pkg_shell_env --> pkg_app_boot
pkg_shell_env --> pkg_home_paths
pkg_shell_env --> pkg_shell
pkg_shell_env --> pkg_tools
@@ -1003,6 +1004,7 @@ flowchart TD
pkg_experimental_auto_review --> pkg_session
pkg_experimental_auto_review --> pkg_tools
pkg_tool_cordis --> pkg_agent
pkg_tool_cordis --> pkg_app_boot
pkg_tool_cordis --> pkg_cordis_host_runner
pkg_tool_cordis --> pkg_tools
pkg_host_plugin_inventory --> pkg_agent_preset_registry
@@ -1577,7 +1579,7 @@ flowchart TD
| [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy) | `session` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`tools`](../packages/core/tools) |
| [`session-title-all-prompts-llm`](../packages/session/session-title-all-prompts-llm) | `session` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`session-title-llm`](../packages/session/session-title-llm) |
| [`session-title-first-prompt-llm`](../packages/session/session-title-first-prompt-llm) | `session` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`session-title-llm`](../packages/session/session-title-llm) |
| [`shell-env`](../packages/shell/shell-env) | `shell` | [`home-paths`](../packages/util/home-paths), [`shell`](../packages/shell/shell), [`tools`](../packages/core/tools) |
| [`shell-env`](../packages/shell/shell-env) | `shell` | [`app-boot`](../packages/boot/app-boot), [`home-paths`](../packages/util/home-paths), [`shell`](../packages/shell/shell), [`tools`](../packages/core/tools) |
| [`tool-bash-persistent`](../packages/shell/tool-bash-persistent) | `shell` | [`agent`](../packages/core/agent), [`terminal`](../packages/terminal/terminal), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`tool-pwsh-persistent`](../packages/shell/tool-pwsh-persistent) | `shell` | [`agent`](../packages/core/agent), [`terminal`](../packages/terminal/terminal), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`ssh`](../packages/ssh/ssh) | `ssh` | [`brand`](../packages/util/brand), [`fs`](../packages/fs/fs), [`fs-local`](../packages/fs/fs-local), [`fs-sandbox`](../packages/fs/fs-sandbox), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-local`](../packages/sandbox/sandbox-local), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session-projection`](../packages/session/session-projection), [`subprocess`](../packages/subprocess/subprocess), [`subprocess-local`](../packages/subprocess/subprocess-local) |
@@ -1590,7 +1592,7 @@ flowchart TD
| [`api-settings-controller`](../packages/api/settings-controller) | `api` | [`agent-preset-registry`](../packages/preset/agent-preset-registry), [`credentials`](../packages/credentials/credentials), [`native-command`](../packages/util/native-command), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`typert-protocol`](../packages/typert/protocol) |
| [`web-app`](../packages/bundle/web-app) | `bundle` | [`shell-env`](../packages/shell/shell-env), [`system-prompt`](../packages/core/system-prompt) |
| [`experimental-auto-review`](../packages/experimental/auto-review) | `experimental` | [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), [`llm`](../packages/llm/llm), [`permission-presets`](../packages/interaction/permission-presets), [`session`](../packages/core/session), [`tools`](../packages/core/tools) |
| [`tool-cordis`](../packages/extensions/tool-cordis) | `extensions` | [`agent`](../packages/core/agent), [`cordis-host-runner`](../packages/extensions/cordis-host-runner), [`tools`](../packages/core/tools) |
| [`tool-cordis`](../packages/extensions/tool-cordis) | `extensions` | [`agent`](../packages/core/agent), [`app-boot`](../packages/boot/app-boot), [`cordis-host-runner`](../packages/extensions/cordis-host-runner), [`tools`](../packages/core/tools) |
| [`host-plugin-inventory`](../packages/host/plugin-inventory) | `host` | [`agent-preset-registry`](../packages/preset/agent-preset-registry), [`brand`](../packages/util/brand), [`typert-protocol`](../packages/typert/protocol) |
| [`mcp-client`](../packages/mcp/mcp-client) | `mcp` | [`attachment`](../packages/attachment/attachment), [`llm`](../packages/llm/llm), [`mcp-resources`](../packages/mcp/mcp-resources), [`scope`](../packages/core/scope), [`subprocess`](../packages/subprocess/subprocess), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`agent-preset`](../packages/preset/agent-preset) | `preset` | [`agent-preset-registry`](../packages/preset/agent-preset-registry) |
+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/tool-catalog.md
tool-catalog.md: 104329d7f472dc2273b802f26df9bd830858f187
tool-catalog.zh.md: 336ac892ad399a243dd2eb4921b0fe18e6bd64f1
tool-catalog.md: 8e1f4f7c8c8b36de366aa8a1b795106f4ae32283
tool-catalog.zh.md: 0995196921738ad4b5a5deda0cf48c83b1d4e664
+1 -1
View File
@@ -732,7 +732,7 @@ Source: [`packages/extensions/tool-cordis/src/index.ts`](../packages/extensions/
### `cordis_inspect_query`
Run a read-only query declared by an Inspect Provider. platform, provider, and method must come from cordis_inspect_list, and input must satisfy that method's schema. Use this Tool before writing plugin code to read exact Service methods, Event modes, Builtin signatures, Tool schemas, theme tokens, or live Slot trees and props. Host queries run locally. A Client query waits for the first valid page response and remains pending until a page answers or the Tool is cancelled. This Tool cannot invoke business Service methods or modify the runtime.
Run a read-only query declared by an Inspect Provider. platform, provider, and method must come from cordis_inspect_list, and input must satisfy that method's schema. Use this Tool before writing plugin code to read exact Service methods, Event modes, plugin Config schemas, Tool schemas, theme tokens, or live Slot trees and props. Host queries run locally. A Client query waits for the first valid page response and remains pending until a page answers or the Tool is cancelled. This Tool cannot invoke business Service methods or modify the runtime.
```json
{
+1 -1
View File
@@ -736,7 +736,7 @@ pwsh 工具是 Windows 组合中 bash 执行器 seam 的 PowerShell 方言消费
### `cordis_inspect_query`
执行 Inspect Provider 声明的只读查询。platform、provider 和 method 必须来自 cordis_inspect_list,input 必须符合该方法的 schema。编写插件代码前,用本工具读取准确的 Service 方法、Event 模式、Builtin 签名、Tool schema、主题 token,或实时 Slot 树与 props。Host 查询在本地运行。Client 查询等待页面首个有效响应,直到页面回应或工具取消。本工具不能调用业务 Service 方法或修改运行时。
执行 Inspect Provider 声明的只读查询。platform、provider 和 method 必须来自 cordis_inspect_list,input 必须符合该方法的 schema。编写插件代码前,用本工具读取准确的 Service 方法、Event 模式、插件 Config schema、Tool schema、主题 token,或实时 Slot 树与 props。Host 查询在本地运行。Client 查询等待页面首个有效响应,直到页面回应或工具取消。本工具不能调用业务 Service 方法或修改运行时。
```json
{
+2
View File
@@ -163,6 +163,8 @@
"verify-tool-catalog": "tsx scripts/gen-tool-catalog.ts --check",
"gen-config-catalog": "tsx scripts/gen-config-catalog.ts",
"verify-config-catalog": "tsx scripts/gen-config-catalog.ts --check",
"gen-plugin-packages": "tsx scripts/gen-plugin-packages.ts",
"verify-plugin-packages": "tsx scripts/gen-plugin-packages.ts --check",
"gen-dependency-catalog": "tsx scripts/gen-dependency-catalog.ts",
"verify-dependency-catalog": "tsx scripts/gen-dependency-catalog.ts --check",
"gen-doc-graphs": "tsx scripts/gen-doc-graphs.ts",
+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/boot/app-boot/README.md
README.md: 89dca0593489885bf2a59851e641d7238f2d66e2
README.zh.md: 78c07a21e79d47fbf9b980509bd9a42d17f4fd11
README.md: 5bbe75d641eeeaf3b87c31221b3491ad1408934e
README.zh.md: b3ac89c1cb082880622175b494d540888b19e804
+1 -1
View File
@@ -68,7 +68,7 @@ Before you boot, you can print the exact configuration the app will mount: the d
### Inspecting plugin configuration schemas
`generateConfigSchema` takes a diagnostic bin name, a prepared on-disk profile, ordered patch lists, and an installation anchor, and returns `ConfigSchemaDump`. App-boot owns composition, runtime resolution, and collection diagnostics. The caller owns profile preparation, home/argv layer selection, process streams, and exit policy.
`generateConfigSchema` takes a diagnostic bin name, a prepared on-disk profile, ordered patch lists, and an installation anchor, and returns `ConfigSchemaDump`. App-boot owns composition, runtime resolution, and collection diagnostics. The caller owns profile preparation, home/argv layer selection, process streams, and exit policy. `createConfigProjector`, `isNativeConfigSchema`, and `LOADER_EXPRESSION_SCHEMA` are exported for callers that project one live plugin Config without profile collection; projected value positions reference `#/$defs/loaderExpression`, so the enclosing document must define it.
The generated JSON Schema 2020-12 describes the composed entry list, with `$defs.patchList` for root-tree overlays and shared definitions projected from plugin Config graphs. It includes disabled entries, native groups, and literal YAML/JSON includes; builtin and canonical native package exports are matched using each tree's module-resolution base, including profile-local copies. Custom carriers are not inferred from their config fields. A missing include with literal `initial` entries is expanded in memory without writes. Discovery and projection diagnostics remain in `x-cordis`, including unknown Configs and partial constraints. The [CLI schema-dump reference](../../../apps/cli/reference/README.md#config-schema-dump) owns the output fields and editing semantics.
+1 -1
View File
@@ -68,7 +68,7 @@ profile 是同一套 dsh 安装提供不同应用界面的方式:`web`、`head
### 检查插件配置 schema
`generateConfigSchema` 接收用于诊断的 bin 名称、已准备好的磁盘 profile、有序 patch 列表和安装锚点,返回 `ConfigSchemaDump`。App-boot 负责组合、运行时解析和收集诊断。调用方负责 profile 准备、home/argv 层选择、进程流及退出策略。
`generateConfigSchema` 接收用于诊断的 bin 名称、已准备好的磁盘 profile、有序 patch 列表和安装锚点,返回 `ConfigSchemaDump`。App-boot 负责组合、运行时解析和收集诊断。调用方负责 profile 准备、home/argv 层选择、进程流及退出策略。`createConfigProjector`、`isNativeConfigSchema` 与 `LOADER_EXPRESSION_SCHEMA` 供不经 profile 收集、只投影单个运行中插件 Config 的调用方使用;投影后的取值位置会引用 `#/$defs/loaderExpression`,外层文档必须定义它。
生成的 JSON Schema 2020-12 描述组合后的 entry list,以 `$defs.patchList` 描述根树 overlay,并从插件 Config 图投影共享定义。它包含禁用项、原生 group 和字面量 YAML/JSON include;内置与规范原生包导出按每棵树的模块解析基准匹配,包括 profile 本地副本。不会根据 config 字段猜测自定义承载插件。include 缺失但有字面量 `initial` 条目时,只在内存中展开,不写文件。发现和投影诊断保留在 `x-cordis` 中,包括未知 Config 和部分约束。[CLI schema dump 参考](../../../apps/cli/reference/README.zh.md#config-schema-dump)负责说明输出字段和编辑语义。
@@ -1,6 +1,6 @@
/** Compose Loader entry/patch structure and discovered plugin input schemas into one JSON Schema document. */
import { createConfigProjector } from './projector.ts'
import { createConfigProjector, LOADER_EXPRESSION_SCHEMA } from './projector.ts'
import type {
CollectedConfigEntry, ConfigJsonSchema, ConfigJsonSchemaObject, ConfigSchemaDiagnostic,
ConfigSchemaDump, ConfigSchemaEntry, NativeConfigSchema,
@@ -48,10 +48,7 @@ export async function buildConfigSchemaDocument(
const project = await createConfigProjector()
const diagnostics = [...initialDiagnostics]
const definitions: Record<string, ConfigJsonSchema> = {
loaderExpression: {
type: 'object', properties: { __jsExpr: { type: 'string' } }, required: ['__jsExpr'],
description: 'Inert representation of a YAML !!js scalar from the Cordis entry-list parser. Its result is evaluated and validated only at runtime. Extra marker-object fields are ignored by interpolation.',
},
loaderExpression: LOADER_EXPRESSION_SCHEMA,
entryMetadata: { type: 'object', properties: metadata() },
entryList: { type: 'array', items: ref('entry') },
patchList: { type: 'array', items: ref('patch') },
@@ -4,7 +4,7 @@ import type { PatchOptions } from '@deepseek-ai/cordis-plugin-include'
import { composeEntries, createRuntimeResolution, readProfileManifest, skippedProfileBundles, type Profile } from '../profile.ts'
import { collectConfigSchemas } from './collect.ts'
import type { ConfigSchemaDiagnostic, ConfigSchemaDump } from './types.ts'
export type { ConfigSchemaDump } from './types.ts'
export type { ConfigSchemaDump, NativeConfigSchema } from './types.ts'
/**
* Generate JSON Schema for a prepared profile's ordered patch layers without mounting plugins or evaluating expressions.
@@ -4,9 +4,16 @@ import type { ConfigJsonSchema, ConfigJsonSchemaObject, NativeConfigSchema } fro
import { isNativeConfigSchema } from './native.ts'
import { createPatternCheck } from './pattern.ts'
/** Definition that projected value positions reference as `#/$defs/loaderExpression`; an enclosing document must define it. */
export const LOADER_EXPRESSION_SCHEMA: ConfigJsonSchemaObject = {
type: 'object', properties: { __jsExpr: { type: 'string' } }, required: ['__jsExpr'],
description: 'Inert representation of a YAML !!js scalar from the Cordis entry-list parser. Its result is evaluated and validated only at runtime. Extra marker-object fields are ignored by interpolation.',
}
/** One Config projection; unknown omission behavior is explicit rather than an invented default. */
export interface ConfigProjection {
schema: ConfigJsonSchema
/** Object-form root: value positions are wrapped in `anyOf` with the loader-expression reference. */
schema: ConfigJsonSchemaObject
definitions: Record<string, ConfigJsonSchema>
acceptsMissing: boolean | 'unknown'
limitations: string[]
+3 -1
View File
@@ -20,7 +20,9 @@ import { createLaunchEnvironmentSnapshot, type LaunchEnvironmentSnapshot } from
export { readProfilePatches, resolveTelemetryPatch, type ProfileContext, type ProfilePnpmInvocation } from './profile-context.ts'
export { sanitizeProfile } from './profile-sanitize.ts'
export { readPluginMeta } from './package-meta.ts'
export { generateConfigSchema, type ConfigSchemaDump } from './config-schema/index.ts'
export { generateConfigSchema, type ConfigSchemaDump, type NativeConfigSchema } from './config-schema/index.ts'
export { createConfigProjector, LOADER_EXPRESSION_SCHEMA, type ConfigProjection } from './config-schema/projector.ts'
export { isNativeConfigSchema } from './config-schema/native.ts'
import type {} from '@deepseek-ai/dsh-system-prompt'
export {
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/client/ui-agent-preset/README.md
README.md: 9bc318e0a80abbf9562b29bc39701c4ce7edac55
README.zh.md: 56da309c0f50e0510d941146d572db969dbe0c8b
README.md: 176957e32695debf6a8aedd98368ec0d985d9182
README.zh.md: a9496780ce5ea48ae95420d11d9f182134518b93
+1 -1
View File
@@ -25,7 +25,7 @@ Choose Agent presets and the new-task default in Web, and read what each mode do
<a id="use-this-package"></a>
## Use this package
Settings shows the built-in and custom card groups with default highlighting and card-body selection; a group without presets is omitted. The page edits nothing: the question icon beside the title opens a dialog explaining that presets are declared by installed bundles and, when the `cordis` preset is on the roster and a conversation flow exists, offers to start a Creator-mode task that authors or overrides a preset as a bundle.
Settings shows the built-in and custom card groups with default highlighting and card-body selection; a group without presets is omitted, except the custom group, which keeps its Creator entry on screen. The page edits nothing: that entry starts a Creator-mode task that authors or overrides a preset as a bundle, offered while the `cordis` preset is on the roster and a conversation flow exists.
The “Choose a mode for new tasks” switch controls whether the saved user default is active. Hiding selection uses the deployment default; showing it restores the user preference. Choosing a healthy default also synchronizes the blank session on the current new-task surface. Creator starts a new task using the `cordis` preset. The new-session picker additionally requires Developer tools in General Settings.
+1 -1
View File
@@ -25,7 +25,7 @@ kind: "package-reference"
<a id="use-this-package"></a>
## 使用此包
设置页显示内置与自定义卡片分组、默认项高亮和点击卡片选择;没有 preset 的分组不显示。本页不编辑任何内容:标题旁的问号图标打开一个对话框,说明 preset 由已安装的 bundle 声明;当 `cordis` preset 在列表中且存在会话流程时,可从对话框启动一个创造模式任务,以 bundle 形式创建或覆盖 preset。
设置页显示内置与自定义卡片分组、默认项高亮和点击卡片选择;没有 preset 的分组不显示,但自定义分组会保留其创造入口。本页不编辑任何内容:该入口启动一个创造模式任务,以 bundle 形式创建或覆盖 preset;当 `cordis` preset 在列表中且存在会话流程时提供。
「新任务可选择模式」开关控制保存的用户默认值是否生效。关闭时使用部署默认值,重新开启则恢复用户偏好。选择健康定义作为默认值也会同步当前新任务页面的空白会话。Creator 入口开启一个使用 `cordis` preset 的新任务。新会话选择器还要求通用设置开启开发者工具。
@@ -6,12 +6,6 @@
color: var(--dsw-alias-label-primary);
}
.titleRow {
display: flex;
align-items: center;
gap: 6px;
}
.title {
margin: 0;
font-size: 18px;
@@ -149,10 +143,10 @@
}
/* The discovery-reported reason, verbatim: it names the rows and the fix.
A real element rather than the icon row's `data-tip` pseudo-element, for two
reasons: generated content joins the card's accessible text, where this would
repeat what the alert already carries, and that tooltip is one `nowrap` line
for an icon label while this names package specifiers one per line. */
A real element rather than a generated tooltip, for two reasons: generated
content joins the card's accessible text, where this would repeat what the
alert already carries, and a tooltip is one `nowrap` line while this names
package specifiers one per line. */
.brokenTip {
position: absolute;
top: calc(100% + 6px);
@@ -323,61 +317,38 @@
color: var(--dsw-alias-label-primary);
}
/* Icon-only actions: the label rides `title` so the row stays quiet until
someone reaches for it. */
.iconButton {
position: relative;
appearance: none;
border: 0;
border-radius: 7px;
padding: 6px;
background: none;
color: var(--dsw-alias-label-tertiary);
cursor: pointer;
display: inline-flex;
align-items: center;
}
.iconButton:disabled {
opacity: 0.4;
cursor: default;
}
.iconButton:hover:not(:disabled) {
background: var(--dsw-alias-bg-layer-1);
color: var(--dsw-alias-label-primary);
}
.iconButton:focus-visible {
outline: 2px solid var(--dsw-alias-brand-primary);
outline-offset: -1px;
}
.iconButton::after {
content: attr(data-tip);
position: absolute;
bottom: calc(100% + 6px);
left: 50%;
transform: translateX(-50%);
padding: 3px 8px;
border-radius: 6px;
background: var(--dsw-alias-label-primary);
color: var(--dsw-alias-bg-layer-3);
font-size: 11px;
line-height: 17px;
white-space: nowrap;
opacity: 0;
pointer-events: none;
transition: opacity .12s;
}
.iconButton:hover::after,
.iconButton:focus-visible::after {
opacity: 1;
}
.error {
margin: 0;
font-size: 12px;
color: var(--dsw-alias-state-error-primary);
}
/* The conversational authoring entry, after the card grid. Dashed like the
Models page's add affordances: it reads as a place a preset will appear,
not a command. */
.creatorButton {
box-sizing: border-box;
align-self: stretch;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
height: 44px;
border: 1px dashed var(--dsw-alias-border-l3);
border-radius: 20px;
font: inherit;
font-size: 14px;
line-height: 22px;
background: transparent;
color: var(--dsw-alias-label-primary);
cursor: pointer;
}
.creatorButton:hover:not(:disabled) {
background: var(--dsw-alias-interactive-bg-hover);
}
.creatorButton:disabled {
opacity: 0.4;
cursor: default;
}
@@ -1,7 +1,7 @@
/** Preset selection settings: the roster, its default, mode help and the Creator-mode entry. */
import type { ReactNode } from 'react'
import { useEffect, useLayoutEffect, useRef, useState } from 'react'
import { Button, IconQuestionOutlineRegular, Modal, Switch, Tag, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives'
import { Button, IconPlusOutlineRegular, Switch, Tag, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives'
import type { ObservableSnapshot, SnapshotStore } from '@deepseek-ai/dsh-client-store'
import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
import type { AgentPresetSectionState } from './section-store.ts'
@@ -60,7 +60,6 @@ export function AgentPresetSection({
}: AgentPresetSectionProps) {
const state = useAgentPresetSection(value => value)
const developerTools = useDeveloperTools(enabled => enabled)
const [authoring, setAuthoring] = useState(false)
const [guide, setGuide] = useState<{
content: NonNullable<ReturnType<typeof presetGuide>>
page: PresetGuidePage
@@ -69,14 +68,24 @@ export function AgentPresetSection({
// Creator mode authors presets in conversation; it needs the flow to land a
// session in and the self-referential preset on the roster.
const creator = startCreatorDraft !== undefined && state.rows.some(row => row.id === 'cordis') ? startCreatorDraft : undefined
return <section className={css.section}>
<div className={css.titleRow}>
<h2 className={css.title}>{t('nav')}</h2>
<button type="button" className={css.iconButton} data-tip={t('authoringHelp')} aria-label={t('authoringHelp')}
onClick={() => { setAuthoring(true) }}>
<IconQuestionOutlineRegular />
/* The custom group is where a preset of one's own appears, so its entry
stays on screen even while the group is empty. */
const creatorButton = creator === undefined
? null
: (
<button
type="button"
className={css.creatorButton}
disabled={!state.showPicker || state.policySaving}
title={state.showPicker ? undefined : t('enablePickerToCreate')}
onClick={() => { creator(); closeSettings() }}
>
<IconPlusOutlineRegular size={14} />
{t('creatorDraft')}
</button>
</div>
)
return <section className={css.section}>
<h2 className={css.title}>{t('nav')}</h2>
<p className={css.intro}>{t('sectionIntro')}</p>
{developerTools && (
<div className={css.pickerPreference}>
@@ -94,10 +103,11 @@ export function AgentPresetSection({
{state.error === null ? null : <p className={css.error} role="alert">{state.error}</p>}
{([true, false] as const).map((builtIn) => {
const rows = state.rows.filter(row => isBuiltInPreset(row) === builtIn)
if (rows.length === 0) return null
const entry = builtIn ? null : creatorButton
if (rows.length === 0 && entry === null) return null
return <section key={String(builtIn)} className={css.group}>
<h3 className={css.groupHead}>{t(builtIn ? 'builtInGroup' : 'customGroup')}</h3>
<ul className={css.cards}>
{rows.length === 0 ? null : <ul className={css.cards}>
{rows.map((row) => {
const display = presetDisplayText(row, t)
const help = presetGuide(row.id, builtIn ? 'system' : 'user')
@@ -138,18 +148,10 @@ export function AgentPresetSection({
</div>}
</li>
})}
</ul>
</ul>}
{entry}
</section>
})}
{guide === null ? null : <PresetGuideDialog guide={guide.content} initialPage={guide.page} t={t} onClose={() => { setGuide(null) }} />}
<Modal open={authoring} title={t('authoringTitle')} closeLabel={t('close')} onClose={() => { setAuthoring(false) }}
footer={<>
<Button variant="outline" onClick={() => { setAuthoring(false) }}>{t('close')}</Button>
{creator === undefined ? null : <Button disabled={!state.showPicker || state.policySaving}
title={state.showPicker ? undefined : t('enablePickerToCreate')}
onClick={() => { setAuthoring(false); creator(); closeSettings() }}>{t('creatorDraft')}</Button>}
</>}>
<p>{t('authoringBody')}</p>
</Modal>
</section>
}
@@ -1,8 +1,8 @@
/**
* Agent-preset surface plugin, browser half — three surfaces over one roster:
* a chip on the new-session screen for the session about to start, a
* read-only label in the session header, and a settings section that manages
* the roster (copy, delete, default, and the way into a preset's own files).
* read-only label in the session header, and a settings section that lists
* the roster (selection, the new-task default, and the way into Creator mode).
*
* A running session keeps the composition it began with (the host refuses to
* adopt an existing session under a different preset). That is what splits
@@ -11,9 +11,6 @@ export type AgentPresetSettingsKey =
| 'headerHint'
| 'nav'
| 'sectionIntro'
| 'authoringHelp'
| 'authoringTitle'
| 'authoringBody'
| 'setDefault'
| 'presetStandardName'
| 'presetStandardDescription'
@@ -41,9 +38,6 @@ export const en: Record<AgentPresetSettingsKey, string> = {
...guideEn,
builtInGroup: 'Built-in', customGroup: 'Custom',
sectionIntro: 'Choose the agent’s tools and how it works. Use Standard mode for everyday tasks, or Creator mode to add capabilities to DSH.',
authoringHelp: 'How to create or change a preset',
authoringTitle: 'Creating and changing presets',
authoringBody: 'Presets are declared by installed bundles, so this page only chooses among them. To create a preset or change one, start a task in Creator mode: the agent writes a bundle that declares or overrides the preset and installs it into the current profile.',
seatHint: 'Choose the agent preset for your new task',
headerHint: 'The agent preset chosen when this task started',
@@ -89,9 +83,6 @@ export const zh: Record<AgentPresetSettingsKey, string> = {
...guideZh,
builtInGroup: '内置', customGroup: '自定义',
sectionIntro: '选择 Agent 的工具和工作方式。日常任务用「标准模式」,扩展 DSH 的能力用「创造模式」。',
authoringHelp: '如何创建或修改预设',
authoringTitle: '创建和修改预设',
authoringBody: '预设由已安装的 bundle 声明,本页只负责选择。要创建或修改预设,请用「创造模式」开始一个任务:Agent 会编写一个声明或覆盖该预设的 bundle,并安装到当前 profile。',
seatHint: '选择新任务使用的 Agent 预设',
headerHint: '本任务的 Agent 预设,在任务开始时确定',
@@ -57,39 +57,31 @@ it('omits an empty group instead of leaving a heading behind', () => {
expect(screen.getByRole('heading', { name: en.builtInGroup })).toBeTruthy()
expect(screen.queryByRole('heading', { name: en.customGroup })).toBeNull()
})
it('explains that presets come from bundles and launches Creator from the guidance', () => {
it('keeps the custom group and its Creator entry while the roster has none', () => {
view({ rows: [{ id: 'cordis', isDefault: true }] }, vi.fn())
const group = screen.getByRole('heading', { name: en.customGroup }).closest('section')
expect(group).not.toBeNull()
expect(within(group!).queryByRole('list')).toBeNull()
expect(within(group!).getByRole('button', { name: en.creatorDraft })).toBeTruthy()
})
it('launches a Creator-mode task from the entry and closes the settings dialog', () => {
const launch = vi.fn()
const actions = view({ rows: [{ id: 'cordis', isDefault: true }] }, launch)
fireEvent.click(screen.getByRole('button', { name: en.authoringHelp }))
const dialog = screen.getByRole('dialog', { name: en.authoringTitle })
expect(within(dialog).getByText(en.authoringBody)).toBeTruthy()
fireEvent.click(within(dialog).getByRole('button', { name: en.creatorDraft }))
fireEvent.click(screen.getByRole('button', { name: en.creatorDraft }))
expect(launch).toHaveBeenCalledOnce()
expect(actions.close).toHaveBeenCalledOnce()
expect(screen.queryByRole('dialog', { name: en.authoringTitle })).toBeNull()
})
it('offers no Creator action without the conversation flow or the cordis preset', () => {
it('offers no Creator entry without the conversation flow or the cordis preset', () => {
view({ rows: [{ id: 'cordis', isDefault: true }] })
fireEvent.click(screen.getByRole('button', { name: en.authoringHelp }))
expect(within(screen.getByRole('dialog')).queryByRole('button', { name: en.creatorDraft })).toBeNull()
fireEvent.click(within(screen.getByRole('dialog')).getAllByRole('button', { name: en.close }).at(-1)!)
expect(screen.queryByRole('button', { name: en.creatorDraft })).toBeNull()
cleanup()
view({}, vi.fn())
fireEvent.click(screen.getByRole('button', { name: en.authoringHelp }))
expect(within(screen.getByRole('dialog')).queryByRole('button', { name: en.creatorDraft })).toBeNull()
expect(screen.queryByRole('button', { name: en.creatorDraft })).toBeNull()
})
it('dismisses the guidance from its header close', () => {
view()
fireEvent.click(screen.getByRole('button', { name: en.authoringHelp }))
const dialog = screen.getByRole('dialog', { name: en.authoringTitle })
fireEvent.click(within(dialog).getAllByRole('button', { name: en.close })[0]!)
expect(screen.queryByRole('dialog', { name: en.authoringTitle })).toBeNull()
})
it('disables Creator in the guidance when mode selection is hidden', () => {
it('disables the Creator entry when mode selection is hidden', () => {
const launch = vi.fn()
view({ showPicker: false, rows: [{ id: 'cordis', isDefault: true }] }, launch)
fireEvent.click(screen.getByRole('button', { name: en.authoringHelp }))
const button = within(screen.getByRole('dialog')).getByRole<HTMLButtonElement>('button', { name: en.creatorDraft })
const button = screen.getByRole<HTMLButtonElement>('button', { name: en.creatorDraft })
expect(button.disabled).toBe(true)
expect(button.title).toBe(en.enablePickerToCreate)
fireEvent.click(button)
@@ -45,7 +45,6 @@ export type {
} from './registry.ts'
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.
@@ -14,34 +14,6 @@
import { createContext, runInContext, Script } from 'node:vm'
import { sandboxDefineTool, sandboxRegisterTool } from './guard.ts'
/** Exact Host closure symbols exposed by the sandbox and guarded Context. */
export const HOST_BUILTIN_INSPECTION = [
{
name: 'ctx',
description: 'Restricted Cordis Context. Prefer ctx.get(name) with an undefined check; use inject for hard dependencies.',
signatures: [
'ctx.get(name: string): unknown | undefined',
'ctx.on(name: string, listener: Function): () => void',
'ctx.provide(name: string, value: unknown): () => void',
'ctx.effect(callback: Function, label?: string): () => void',
],
},
{
name: 'harness',
description: 'Host helpers for Package-private Client RPC and model-visible dynamic Tools.',
signatures: [
'harness.handle(method: string, handler: (args: JsonValue) => JsonValue | Promise<JsonValue>): () => void',
'harness.defineTool(definition: ToolDefinition): ToolDefinition',
'harness.registerTool(ctx: Context, tool: ToolDefinition): () => void',
],
},
{ name: 'console', description: 'Package-tagged Host logging.', signatures: ['console.log(...values): void', 'console.error(...values): void'] },
{ name: 'btoa', description: 'Encode UTF-8 text as base64.', signatures: ['btoa(value: string): string'] },
{ name: 'atob', description: 'Decode base64 as UTF-8 text.', signatures: ['atob(value: string): string'] },
{ name: 'TextEncoder', description: 'Standard UTF-8 encoder constructor.', signatures: ['new TextEncoder()'] },
{ name: 'TextDecoder', description: 'Standard text decoder constructor.', signatures: ['new TextDecoder(label?: string)'] },
] as const
/**
* A write-through console for one package, tagging every line with the package
* id. Write-through (host stdout/stderr), NOT buffered into the tool result:
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/extensions/tool-cordis/README.md
README.md: 4e7efefd21e3b7cf40e8ba5fe2291d220dfd796f
README.zh.md: 76331c25f0996c053b39e5b227a50afae84e6bb4
README.md: 23e2d4a3d047c1442769e07be045f7da4a24a783
README.zh.md: da855b7b1d4b4287a1f8d3a5ea7a1fb0c9407f2f
+4 -3
View File
@@ -25,7 +25,7 @@ Inspect Host and Client runtime APIs before writing plugin code. Creator mode pr
<a id="use-this-package"></a>
## Use this package
Creator mode includes this toolset. Other compositions mount `@deepseek-ai/dsh-tool-cordis/host` once in the host composition beside the host runner that provides `cordisInspect`, and `@deepseek-ai/dsh-tool-cordis` in each agent preset that exposes the tools; a preset row alone registers no Host providers. Call `cordis_inspect_list` to discover providers, then `cordis_inspect_query` for a provider's exact methods and types. Use [Plugin Manager](../../boot/plugin-manager/README.md) to install bundles containing plugin code or MCP configuration.
Creator mode includes this toolset. Other compositions mount `@deepseek-ai/dsh-tool-cordis/host` once in the host composition beside the host runner that provides `cordisInspect`, and `@deepseek-ai/dsh-tool-cordis` in each agent preset that exposes the tools; a preset row alone registers no Host providers. Call `cordis_inspect_list` to discover providers, then `cordis_inspect_query` for a provider's exact methods and types. The Host `Config` provider lists live Loader entries in pages (`offset`, `limit` up to 100, optional exact plugin `name`; `total` and `nextOffset` bound the walk) with each entry's Loader id, the tree-local id patches address, and its Config status (`schema`, `absent`, `unsupported`, `tree` for group and include carriers, `inactive` for disabled, never imported, or disposed entries), and projects one entry's native Config into a self-contained JSON Schema document beside the entry's `packageDir`, the resolved directory holding the package README and built `lib/`, when the profile package lookup resolves it. Use [Plugin Manager](../../boot/plugin-manager/README.md) to install bundles containing plugin code or MCP configuration.
-----
@@ -35,7 +35,7 @@ Creator mode includes this toolset. Other compositions mount `@deepseek-ai/dsh-t
<details>
<summary>Implementation internals — click to expand</summary>
Host providers combine generated Service/Event catalogs and the requesting agent's tool registry. Client providers synchronize their manifests through the existing inspection registry and answer queries from a connected page. The host entry owns the Host provider registrations and the preset row owns the two tools, each through Cordis effects; the registry rejects a duplicate provider id, which is why the providers register once per process rather than per preset. No invariant companion is published because inspection reads its providers directly and maintains no independent runtime projection.
Host providers combine generated Service/Event catalogs, the live Loader tree projected through the app-boot Config projector, and the requesting agent's tool registry. Client providers synchronize their manifests through the existing inspection registry and answer queries from a connected page. The host entry owns the Host provider registrations and the preset row owns the two tools, each through Cordis effects; the registry rejects a duplicate provider id, which is why the providers register once per process rather than per preset. No invariant companion is published because inspection reads its providers directly and maintains no independent runtime projection.
</details>
@@ -54,7 +54,7 @@ Host providers combine generated Service/Event catalogs and the requesting agent
#### What the model sees
The [tool catalog](../../../docs/tool-catalog.md#deepseek-aidsh-tool-cordis) describes two read-only inspection tools. The plugin contributes no system prompt section: the tool descriptions state when to call each tool and that queries never invoke business methods. In the `cordis` preset, the first-turn skill catalog carries the descriptions of the two shipped skills, which route plugin, MCP, composition, and destination-less visual requests to the skill covering Plugin Manager, MCP setup, Client packaging, and slot registration. Query results contain the requested API declarations or live tool schemas.
The [tool catalog](../../../docs/tool-catalog.md#deepseek-aidsh-tool-cordis) describes two read-only inspection tools. The plugin contributes no system prompt section: the tool descriptions state when to call each tool and that queries never invoke business methods. In the `cordis` preset, the first-turn skill catalog carries the descriptions of the two shipped skills, which route plugin, MCP, composition, and destination-less visual requests to the skill covering Plugin Manager, MCP setup, Client packaging, and slot registration. Query results contain the requested API declarations, live tool schemas, the live entry directory with Config status, or one entry's projected Config JSON Schema.
#### Token effect
@@ -69,6 +69,7 @@ Unchanged tool schemas remain prefix-stable. Query results append to history; en
<a id="known-limitations-and-deferred-work"></a>
- Client queries wait for a responding page or cancellation. Inspection cannot invoke service methods, configure plugins, or execute generated code.
- `Config.listConfigs` walks the profile Loader tree only. Agent preset `plugins` lists mount in detached preset trees, so a plugin present only inside a preset declaration is not listed unless the profile tree also mounts it.
<a id="dev-note"></a>
### Dev Note
+4 -3
View File
@@ -25,7 +25,7 @@ kind: "package-reference"
<a id="use-this-package"></a>
## 使用本包
创造模式包含这组工具。其他组合需要在宿主组合里、提供 `cordisInspect` 的 host runner 旁挂载一次 `@deepseek-ai/dsh-tool-cordis/host`,并在每个要暴露这些工具的 agent preset 里挂载 `@deepseek-ai/dsh-tool-cordis`;仅有 preset 行不会注册任何 Host provider。调用 `cordis_inspect_list` 发现 provider,再用 `cordis_inspect_query` 查询其具体方法和类型。通过 [Plugin Manager](../../boot/plugin-manager/README.zh.md) 安装包含插件代码或 MCP 配置的组合包。
创造模式包含这组工具。其他组合需要在宿主组合里、提供 `cordisInspect` 的 host runner 旁挂载一次 `@deepseek-ai/dsh-tool-cordis/host`,并在每个要暴露这些工具的 agent preset 里挂载 `@deepseek-ai/dsh-tool-cordis`;仅有 preset 行不会注册任何 Host provider。调用 `cordis_inspect_list` 发现 provider,再用 `cordis_inspect_query` 查询其具体方法和类型。Host 的 `Config` provider 分页列出运行中的 Loader entry(`offset`、最多 100 的 `limit`、可选的精确插件 `name`;`total` 与 `nextOffset` 界定遍历),每个 entry 带 Loader id、patch 所寻址的树内 id 及其 Config 状态(`schema`、`absent`、`unsupported`、group 与 include 载体为 `tree`、禁用、未导入或已销毁的 entry 为 `inactive`),并把单个 entry 的原生 Config 投影为自包含的 JSON Schema 文档,同时在 profile 包查找能解析时给出该 entry 的 `packageDir`,即包 README 与构建产物 `lib/` 所在目录。通过 [Plugin Manager](../../boot/plugin-manager/README.zh.md) 安装包含插件代码或 MCP 配置的组合包。
-----
@@ -35,7 +35,7 @@ kind: "package-reference"
<details>
<summary>实现细节 — 点击展开</summary>
Host provider 结合生成的 Service/Event 目录与请求 agent 的工具注册表。Client provider 通过现有检查注册表同步清单,并从已连接页面回答查询。宿主入口持有 Host provider 的注册,preset 行持有两个工具,都通过 Cordis effect;注册表拒绝重复的 provider id,所以 provider 按进程注册一次而不是按 preset 注册。检查直接读取 provider,不维护独立运行时投影,因此不发布不变式配套插件。
Host provider 结合生成的 Service/Event 目录、经 app-boot Config 投影器投影的运行中 Loader 树,以及请求 agent 的工具注册表。Client provider 通过现有检查注册表同步清单,并从已连接页面回答查询。宿主入口持有 Host provider 的注册,preset 行持有两个工具,都通过 Cordis effect;注册表拒绝重复的 provider id,所以 provider 按进程注册一次而不是按 preset 注册。检查直接读取 provider,不维护独立运行时投影,因此不发布不变式配套插件。
</details>
@@ -54,7 +54,7 @@ Host provider 结合生成的 Service/Event 目录与请求 agent 的工具注
#### 模型所见
[工具目录](../../../docs/tool-catalog.zh.md#deepseek-aidsh-tool-cordis) 描述两个只读检查工具。插件不贡献 system prompt 段落:工具描述已说明何时调用每个工具以及查询不会调用业务方法。在 `cordis` preset 中,首轮 skill catalog 携带两个随附技能的描述,把插件、MCP、组合编辑和未指定去向的视觉请求路由到覆盖 Plugin Manager、MCP 设置、Client 打包和 slot 注册的技能。查询结果包含所请求的 API 声明或当前工具 schema。
[工具目录](../../../docs/tool-catalog.zh.md#deepseek-aidsh-tool-cordis) 描述两个只读检查工具。插件不贡献 system prompt 段落:工具描述已说明何时调用每个工具以及查询不会调用业务方法。在 `cordis` preset 中,首轮 skill catalog 携带两个随附技能的描述,把插件、MCP、组合编辑和未指定去向的视觉请求路由到覆盖 Plugin Manager、MCP 设置、Client 打包和 slot 注册的技能。查询结果包含所请求的 API 声明、当前工具 schema、带 Config 状态的运行中 entry 目录,或单个 entry 投影后的 Config JSON Schema。
#### Token 影响
@@ -69,6 +69,7 @@ Host provider 结合生成的 Service/Event 目录与请求 agent 的工具注
<a id="known-limitations-and-deferred-work"></a>
- Client 查询等待页面响应或取消。检查不能调用服务方法、配置插件或执行生成代码。
- `Config.listConfigs` 只遍历 profile 的 Loader 树。Agent preset 的 `plugins` 列表挂载在独立的 preset 树中,所以只出现在 preset 声明里的插件不会被列出,除非 profile 树也挂载了它。
<a id="dev-note"></a>
### 开发备注
+12 -7
View File
@@ -32,17 +32,22 @@
],
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-cordis-host-runner": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/cordis-plugin-loader": "workspace:^",
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-app-boot": "workspace:^",
"@deepseek-ai/dsh-cordis-host-runner": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/cordis-plugin-group": "workspace:^",
"@deepseek-ai/cordis-plugin-loader": "workspace:^",
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-app-boot": "workspace:^",
"@deepseek-ai/dsh-cordis-host-runner": "workspace:^",
"@deepseek-ai/dsh-system-prompt": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
"@deepseek-ai/schemastery": "workspace:^"
}
}
@@ -0,0 +1,120 @@
/** Live plugin Config discovery projected from the running Loader tree. */
import type { Context } from '@deepseek-ai/cordis'
import type { Entry } from '@deepseek-ai/cordis-plugin-loader'
// Declares `Context.pluginPackages`, the profile package lookup that resolves an entry's package directory.
import { createConfigProjector, isNativeConfigSchema, LOADER_EXPRESSION_SCHEMA, type NativeConfigSchema } from '@deepseek-ai/dsh-app-boot'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
/**
* `schema`: the running plugin declares a native Schemastery Config. `absent`: it runs without one.
* `unsupported`: its Config is not a native Schemastery graph. `tree`: a Loader carrier (`cordis:group`,
* `cordis:include`) whose `config` is a child entry list, not a plugin Config. `inactive`: the entry is
* disabled, was never imported, or its fiber has been disposed, so no Config is read. A plugin whose
* `apply` failed keeps its fiber and still reports its Config.
*/
type ConfigStatus = 'schema' | 'absent' | 'unsupported' | 'tree' | 'inactive'
interface LiveConfig {
/** Loader entry id, unique across nested include trees (`parent:child`). */
id: string
/** Tree-local id that profile patches address. */
patchId: string
name: string
status: ConfigStatus
native?: NativeConfigSchema
}
function liveConfig(entry: Entry): LiveConfig {
const { id, name } = entry.options
const listed = { id: entry.id, patchId: id, name }
if (entry.subgroup !== undefined || entry.subtree !== undefined) return { ...listed, status: 'tree' }
const fiber = entry.fiber
if (fiber === undefined || fiber.uid === null || entry.disabled) return { ...listed, status: 'inactive' }
const config: unknown = fiber.runtime?.Config
if (config === undefined || config === null) return { ...listed, status: 'absent' }
if (!isNativeConfigSchema(config)) return { ...listed, status: 'unsupported' }
return { ...listed, status: 'schema', native: config }
}
function listing({ id, patchId, name, status }: LiveConfig): { [key: string]: JsonValue } {
return { id, patchId, name, status }
}
/** Model-supplied query fields, validated at the tool JSON boundary. */
interface ConfigQuery {
entry: string | undefined
name: string | undefined
offset: number
limit: number
}
const DEFAULT_LIMIT = 25
const MAX_LIMIT = 100
function parseQuery(input: JsonValue | undefined): ConfigQuery {
const fields = input !== undefined && input !== null && typeof input === 'object' && !Array.isArray(input) ? input : {}
const text = (key: string): string | undefined => {
const value = fields[key]
if (value === undefined) return undefined
if (typeof value !== 'string' || value === '') throw new Error(`${key} must be a non-empty string`)
return value
}
const offset = fields.offset ?? 0
const limit = fields.limit ?? DEFAULT_LIMIT
if (!Number.isInteger(offset) || typeof offset !== 'number' || offset < 0) throw new Error('offset must be a non-negative integer')
if (!Number.isInteger(limit) || typeof limit !== 'number' || limit < 1 || limit > MAX_LIMIT) {
throw new Error(`limit must be an integer from 1 to ${MAX_LIMIT}`)
}
return { entry: text('entry'), name: text('name'), offset, limit }
}
/** Resolve the package directory behind an entry's plugin name through the profile package lookup. */
function packageDir(ctx: Context, entry: Entry): { packageDir?: string } {
const baseUrl = entry.parent.tree.ctx.baseUrl
const dir = baseUrl === undefined ? undefined : ctx.get('pluginPackages')?.packageOf(entry.options.name, baseUrl)?.dir
return dir === undefined ? {} : { packageDir: dir }
}
/** Projection output is JSON by construction; the CLI schema dump serializes the same values. */
function toJson(value: object): JsonValue {
return JSON.parse(JSON.stringify(value)) as JsonValue
}
/**
* Answer the `Config.listConfigs` inspect query from the live Loader tree.
* Without `entry`, return one page of the directory: each entry's Loader id, patch id, plugin name, and Config
* status, optionally filtered to one exact plugin `name`, with `total` and `nextOffset` (null on the last page).
* With `entry`, return that entry with its resolved `packageDir` (the directory holding the package README and
* built `lib/`, when the profile package lookup resolves it) and project its native Config into one self-contained
* JSON Schema 2020-12 document whose `$defs` carry the shared and `loaderExpression` definitions, plus omission
* acceptance and projection limitations.
* Validators and transform callbacks are not executed; runtime-created Agent preset trees are outside the Loader.
* @param ctx - Host context; its `loader`, when the profile was mounted by one, owns the entry tree.
* @param input - model-supplied query: `entry` (Loader entry id), or `name`, `offset` (default 0), and `limit` (1 to 100, default 25).
* @returns one directory page, or one entry's status and projected schema.
* @throws when no Loader mounted this Host, when a query field is malformed, or when the entry id matches no live entry.
*/
export async function queryLiveConfig(ctx: Context, input: JsonValue | undefined): Promise<JsonValue> {
const loader = ctx.get('loader')
if (loader === undefined) throw new Error('Config inspection requires the Loader that mounted this profile; this Host was composed without one')
const query = parseQuery(input)
const entries = [...loader.entries()].map(liveConfig)
if (query.entry === undefined) {
const matching = query.name === undefined ? entries : entries.filter(candidate => candidate.name === query.name)
const page = matching.slice(query.offset, query.offset + query.limit).map(listing)
const end = query.offset + page.length
return { entries: page, total: matching.length, nextOffset: end < matching.length ? end : null }
}
const entry = entries.find(candidate => candidate.id === query.entry)
if (entry === undefined) throw new Error(`unknown entry id ${JSON.stringify(query.entry)}`)
const listed = { ...listing(entry), ...packageDir(ctx, loader.resolve(query.entry)) }
if (entry.native === undefined) return listed
const project = await createConfigProjector()
const { schema, definitions, acceptsMissing, limitations } = project(entry.native, 'config')
return toJson({ ...listed, acceptsMissing, limitations, schema: {
$schema: 'https://json-schema.org/draft/2020-12/schema',
$defs: { ...definitions, loaderExpression: LOADER_EXPRESSION_SCHEMA },
...schema,
} })
}
+2 -2
View File
@@ -43,8 +43,8 @@ export function apply(ctx: Context): void {
description:
'Run a read-only query declared by an Inspect Provider. platform, provider, and method must come from '
+ 'cordis_inspect_list, and input must satisfy that method\'s schema. Use this Tool before writing plugin code '
+ 'to read exact Service methods, Event modes, Builtin signatures, Tool schemas, theme tokens, or live Slot '
+ 'trees and props. Host queries run locally. A Client query waits for the first valid page response and '
+ 'to read exact Service methods, Event modes, plugin Config schemas, Tool schemas, theme tokens, or live '
+ 'Slot trees and props. Host queries run locally. A Client query waits for the first valid page response and '
+ 'remains pending until a page answers or the Tool is cancelled. This Tool cannot invoke business Service '
+ 'methods or modify the runtime.',
parameters: {
@@ -1,10 +1,10 @@
/** First-party Host inspect providers registered by the Cordis tool package. */
import type { Context } from '@deepseek-ai/cordis'
import { HOST_BUILTIN_INSPECTION } from '@deepseek-ai/dsh-cordis-host-runner'
import type { HostCordisInspectProviderRegistration } from '@deepseek-ai/dsh-cordis-host-runner'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
import { EVENT_API, queryEventApi, queryServiceApi } from './api-catalog.ts'
import { queryLiveConfig } from './config.ts'
const EMPTY_INPUT = { type: 'object', properties: {}, additionalProperties: false } as const
const ANY_OUTPUT = { description: 'JSON data owned by this inspect provider.' } as const
@@ -16,11 +16,24 @@ const SERVICE_OUTPUT = {
const EVENT_OUTPUT = {
description: 'Compact Event directory, or one exact Event contract with only its referenced type declarations.',
} as const
const CONFIG_INPUT: JsonValue = {
type: 'object',
properties: {
entry: { type: 'string', description: 'Exact Loader entry id from the directory; returns that entry\'s projected Config schema.' },
name: { type: 'string', description: 'Exact plugin package name; limits the directory to its entries.' },
offset: { type: 'number', description: 'Zero-based directory offset; defaults to 0.' },
limit: { type: 'number', description: 'Directory page size, from 1 to 100; defaults to 25.' },
},
additionalProperties: false,
}
const CONFIG_OUTPUT = {
description: 'One directory page of live entries with patch ids, Config status, total, and nextOffset, or one entry\'s projected JSON Schema with shared definitions, omission acceptance, and projection limitations.',
} 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.
* @param ctx - Host context used for Agent-scoped live Tool queries.
* @param ctx - Host context used for live Loader Config and Agent-scoped Tool queries.
* @returns registrations for static catalogs and live Host capabilities.
*/
export function hostInspectProviders(ctx: Context): HostCordisInspectProviderRegistration[] {
@@ -41,10 +54,14 @@ export function hostInspectProviders(ctx: Context): HostCordisInspectProviderReg
EVENT_INPUT,
EVENT_OUTPUT,
),
registration('Builtin', 'Plain-JavaScript symbols available to a dynamic Host half.', 'listBuiltins', () => ({
builtins: HOST_BUILTIN_INSPECTION,
referencedTypes: [],
} as unknown as JsonValue)),
registration(
'Config',
'Progressive live plugin Config discovery: paged entry directory with schema status, then one entry\'s exact JSON Schema.',
'listConfigs',
input => queryLiveConfig(ctx, input),
CONFIG_INPUT,
CONFIG_OUTPUT,
),
{
manifest: {
id: 'Tool',
@@ -0,0 +1,128 @@
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { pathToFileURL } from 'node:url'
import { Context } from '@deepseek-ai/cordis'
import Group from '@deepseek-ai/cordis-plugin-group'
import Loader from '@deepseek-ai/cordis-plugin-loader'
import { PluginPackages } from '@deepseek-ai/dsh-app-boot'
import z from '@deepseek-ai/schemastery'
import { describe, expect, it, onTestFinished, vi } from 'vitest'
import { queryLiveConfig } from '../src/config.ts'
/** The Config provider reads the running Loader tree: only live fibers expose a Config, carriers never do. */
const modules: Record<string, unknown> = {
'cordis:group': Group,
'with-schema': { name: 'with-schema', apply() {}, Config: z.object({ port: z.number().default(80).description('Listen port.') }) },
'no-config': { name: 'no-config', apply() {} },
'foreign-schema': { name: 'foreign-schema', apply() {}, Config: { '~standard': { validate: (value: unknown) => ({ value }) } } },
}
interface Ids {
withSchema: string
noConfig: string
foreign: string
off: string
group: string
}
/** A Loader tree whose base directory holds `node_modules/with-schema/package.json`, so the package lookup resolves that plugin. */
async function loaded(): Promise<{ ctx: Context; ids: Ids; packageDir: string }> {
const dir = mkdtempSync(join(tmpdir(), 'dsh-tool-cordis-config-'))
onTestFinished(() => { rmSync(dir, { recursive: true, force: true }) })
const packageDir = join(dir, 'node_modules', 'with-schema')
mkdirSync(packageDir, { recursive: true })
writeFileSync(join(packageDir, 'package.json'), '{"name":"with-schema","version":"1.0.0"}\n')
const ctx = new Context()
ctx.baseUrl = `${pathToFileURL(dir).href}/`
await ctx.plugin(PluginPackages)
await ctx.plugin(Loader)
vi.spyOn(ctx.loader, 'import').mockImplementation((name: string) => Promise.resolve(modules[name]))
const ids: Ids = {
withSchema: await ctx.loader.create({ name: 'with-schema' }),
noConfig: await ctx.loader.create({ name: 'no-config' }),
foreign: await ctx.loader.create({ name: 'foreign-schema' }),
off: await ctx.loader.create({ name: 'with-schema', disabled: true }),
group: await ctx.loader.create({ name: 'cordis:group', group: true, config: [{ id: 'nested', name: 'no-config' }] }),
}
await ctx.loader.await()
return { ctx, ids, packageDir }
}
interface Page { entries: Array<{ id: string; name: string }>; total: number; nextOffset: number | null }
/** Directory pages are plain JSON; a round trip gives the test its typed view. */
function asPage(value: unknown): Page {
return JSON.parse(JSON.stringify(value)) as Page
}
async function directory(ctx: Context): Promise<Record<string, unknown>> {
const { entries } = asPage(await queryLiveConfig(ctx, { limit: 100 }))
return Object.fromEntries(entries.map(entry => [entry.id, entry]))
}
describe('the Config inspect provider', () => {
it('lists every live entry with its Loader id, patch id, and Config status', async () => {
const { ctx, ids } = await loaded()
const listed = await directory(ctx)
expect(Object.keys(listed).sort()).toEqual([ids.withSchema, ids.noConfig, ids.foreign, ids.off, ids.group, 'nested'].sort())
expect(listed[ids.withSchema]).toEqual({ id: ids.withSchema, patchId: ids.withSchema, name: 'with-schema', status: 'schema' })
expect(listed[ids.noConfig]).toMatchObject({ status: 'absent' })
expect(listed[ids.foreign]).toMatchObject({ status: 'unsupported' })
expect(listed[ids.off]).toMatchObject({ name: 'with-schema', status: 'inactive' })
expect(listed[ids.group]).toMatchObject({ name: 'cordis:group', status: 'tree' })
expect(listed.nested).toMatchObject({ patchId: 'nested', status: 'absent' })
await ctx.fiber.dispose()
})
it('pages the directory, filters it by plugin name, and rejects malformed query fields', async () => {
const { ctx, ids } = await loaded()
const first = asPage(await queryLiveConfig(ctx, { limit: 2 }))
expect(first.entries).toHaveLength(2)
expect(first).toMatchObject({ total: 6, nextOffset: 2 })
const last = asPage(await queryLiveConfig(ctx, { offset: 4, limit: 2 }))
expect(last).toMatchObject({ total: 6, nextOffset: null })
const named = asPage(await queryLiveConfig(ctx, { name: 'with-schema' }))
expect(named.entries.map(entry => entry.id).sort()).toEqual([ids.withSchema, ids.off].sort())
expect(named).toMatchObject({ total: 2, nextOffset: null })
await expect(queryLiveConfig(ctx, { limit: 0 })).rejects.toThrow('limit must be an integer from 1 to 100')
await expect(queryLiveConfig(ctx, { offset: -1 })).rejects.toThrow('offset must be a non-negative integer')
await expect(queryLiveConfig(ctx, { name: '' })).rejects.toThrow('name must be a non-empty string')
await ctx.fiber.dispose()
})
it('reports a plugin disabled after it ran as inactive', async () => {
const { ctx, ids } = await loaded()
await ctx.loader.resolve(ids.withSchema).update({ disabled: true })
await ctx.loader.await()
expect((await directory(ctx))[ids.withSchema]).toMatchObject({ status: 'inactive' })
expect(await queryLiveConfig(ctx, { entry: ids.withSchema })).not.toHaveProperty('schema')
await ctx.fiber.dispose()
})
it('projects one entry with its package directory and reports status alone without a native Config', async () => {
const { ctx, ids, packageDir } = await loaded()
const projected = await queryLiveConfig(ctx, { entry: ids.withSchema })
expect(projected).toMatchObject({
id: ids.withSchema, status: 'schema', packageDir, acceptsMissing: true, limitations: [],
schema: {
$schema: 'https://json-schema.org/draft/2020-12/schema',
$defs: { loaderExpression: { type: 'object', required: ['__jsExpr'] } },
anyOf: [
{ type: ['object', 'null'], properties: { port: { default: 80, description: 'Listen port.' } } },
{ $ref: '#/$defs/loaderExpression' },
],
},
})
expect(await queryLiveConfig(ctx, { entry: ids.off })).toEqual({ id: ids.off, patchId: ids.off, name: 'with-schema', status: 'inactive', packageDir })
expect(await queryLiveConfig(ctx, { entry: ids.noConfig })).not.toHaveProperty('packageDir')
expect(await queryLiveConfig(ctx, { entry: ids.group })).toEqual({ id: ids.group, patchId: ids.group, name: 'cordis:group', status: 'tree' })
await expect(queryLiveConfig(ctx, { entry: 'missing' })).rejects.toThrow('unknown entry id "missing"')
await ctx.fiber.dispose()
})
it('rejects a Host composed without a Loader', async () => {
await expect(queryLiveConfig(new Context(), undefined)).rejects.toThrow('requires the Loader')
})
})
@@ -27,7 +27,7 @@ describe('the cordis-inspect-providers host entry', () => {
const fiber = ctx.plugin(CordisInspectProviders)
await fiber
expect(ctx.cordisInspect.list().map(provider => provider.id)).toEqual(['Service', 'Event', 'Builtin', 'Tool'])
expect(ctx.cordisInspect.list().map(provider => provider.id)).toEqual(['Service', 'Event', 'Config', 'Tool'])
await fiber.dispose()
expect(ctx.cordisInspect.list()).toEqual([])
@@ -28,6 +28,18 @@
},
{
"path": "../cordis-host-runner"
},
{
"path": "../../../vendor/loader"
},
{
"path": "../../boot/app-boot"
},
{
"path": "../../util/values"
},
{
"path": "../../../vendor/group"
}
]
}
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/preset/agent-preset/README.md
README.md: b9274969b5c219e9120ba754864e87c03c409bcb
README.zh.md: aba74e630713291d1f17a4fe5668aed8985521ea
README.md: 99802e2592e046c0580a77b5891a5b79c1e3f1f5
README.zh.md: 1130b015a8f7bafcf9b675abd3ca6d654d8e2d7f
+2
View File
@@ -47,6 +47,8 @@ Define an Agent’s child plugins in ordinary Cordis YAML. Declare several prese
The declaration row’s `id` addresses Loader edits; `config.id` is the preset identity saved by sessions. Child entry IDs may be omitted and assigned by Loader.
The package also ships the `skills/` directory that creator mode mounts through `skill-filesystem`: `cordis-plugin-development` (a short procedure with `references/` read on demand and copyable `templates/` bundles), `editing-cordis-compositions`, and `cordis-composition-reference`, whose `references/packages.md` is generated by `scripts/gen-plugin-packages.ts` and freshness-gated in `doc-sync`. The two procedure skills tell the agent to read inspection results first, then the package README under the `packageDir` that `Config.listConfigs` reports, then built `lib/` or checkout sources. Every shipped `SKILL.md`, rendered as a `skill` tool result, stays under the 8192-character threshold above which the standard preset's tool-result pruner trims results.
<a id="understand-the-implementation"></a>
## Understand the implementation
@@ -47,6 +47,8 @@ kind: "package-reference"
声明行的 `id` 是 Loader 编辑地址;`config.id` 是会话保存的 preset 标识符。子插件可省略行 ID,由 Loader 分配。
本包还随包出货创造模式通过 `skill-filesystem` 挂载的 `skills/` 目录:`cordis-plugin-development`(简短流程,配合按需读取的 `references/` 与可复制的 `templates/` 组合包)、`editing-cordis-compositions`,以及 `cordis-composition-reference`,后者的 `references/packages.md` 由 `scripts/gen-plugin-packages.ts` 生成并在 `doc-sync` 中做新鲜度门禁。两个流程 skill 让 agent 先读检查结果,再读 `Config.listConfigs` 报告的 `packageDir` 下的包 README,最后读构建后的 `lib/` 或 checkout 源码。每个出货的 `SKILL.md` 作为 `skill` 工具结果渲染后都保持在 8192 字符阈值之下,标准 preset 的工具结果修剪器超过该阈值才会裁剪。
<a id="understand-the-implementation"></a>
## 理解实现
+6 -1
View File
@@ -37,8 +37,13 @@
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/cordis-plugin-include": "workspace:^",
"@deepseek-ai/cordis-plugin-loader": "workspace:^",
"@deepseek-ai/dsh-agent-preset-registry": "workspace:^",
"@deepseek-ai/schemastery": "workspace:^"
"@deepseek-ai/dsh-compaction-tool-result-pruner": "workspace:^",
"@deepseek-ai/dsh-skill": "workspace:^",
"@deepseek-ai/schemastery": "workspace:^",
"@types/js-yaml": "^4.0.9",
"js-yaml": "^4.2.0"
}
}
@@ -0,0 +1,29 @@
---
name: cordis-composition-reference
description: Use when writing or reading a Cordis bundle patch or agent preset for this harness and you need the Loader YAML dialect (insert, override by id, group, disabled, isolate, !!js) or the list of installable Harness plugin packages and what each provides.
---
# Cordis composition reference
Reference material for bundle patches and preset plugin lists. Procedures live in `cordis-plugin-development` and `editing-cordis-compositions`.
## Loader patch dialect
A profile composes an ordered list of patch layers over the bundle entry lists. Each patch is a mapping:
- `insert: [rows]` appends rows; with an `id` naming an existing `group: true` row, the rows are appended inside that group's `config` list.
- A patch with an `id` and no `insert` targets the existing row with that id. Supplied fields replace the row's fields; `config` is replaced wholesale, never deep-merged, so restate every field the row needs. A truthy `name` asserts the existing plugin name rather than renaming it.
- Non-insert patches without a nonempty `id`, and targets that match no row, are warned about and skipped.
A row has `id`, `name` (the plugin package specifier; inserted relative paths are anchored beside their patch file), optional `config`, and optional `disabled`, `inject`, `intercept`, and `isolate`.
- `group: true` with `name: cordis:group` makes `config` a nested entry list and allows patches to insert into it by id. `cordis:include` loads a literal YAML or JSON entry list from `config.path`.
- `disabled` accepts a boolean, null, or a `!!js` expression evaluated against the Loader context at every mount decision. A disabled row omits required `config` unless `group: true` forces activation.
- `!!js` scalars are Loader expressions, never `!js`. Inside `config` they are evaluated after the row's declared injections activate, against that plugin's context (`ctx.<service>`), so `!!js dshHomePath('sessions')` and `!!js "!ctx.get('profileContext')"` are valid. Other row metadata stays literal.
- `isolate` maps service names to `true` or a realm label; a preset plugin that provides a service isolates the provider and all consumers together. Scope controls contributions and event visibility; `isolate` controls service instances.
`plugin_manager` `list_plugins` reports each row's `enabled` flag and `fiberPhase`; `set_plugin` and `set_bundle` answer `overridden` when a higher-priority layer wins.
## Installable plugin packages
`references/packages.md` lists every workspace package that exports a Cordis plugin, grouped by package group, with whether its row takes `config`. Search it with `grep -n <keyword>` rather than reading it whole. Plugins outside this list come from installed bundles; `plugin_manager` `list_bundles` names them.
@@ -0,0 +1,501 @@
# Loadable Harness plugin packages
This file is GENERATED from workspace manifests (`scripts/gen-plugin-packages.ts`) and verified fresh by `pnpm run verify-plugin-packages` (part of `doc-sync`); do not edit it by hand.
Every package below exports a Cordis plugin that a bundle patch can name in a Loader row. `Config` marks packages whose row accepts a `config` mapping; query `Config.listConfigs` through `cordis_inspect_query` (filter by `name`, then query the `entry` id) for the mounted schema. Packages under `experimental` are pre-stable.
## acp
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-acp` | yes | Automation-only Agent Client Protocol server for driving DeepSeek Harness agents over JSON-RPC stdio |
## api
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-api-account-controller` | no | Expose safe account operations over authenticated Remote |
| `@deepseek-ai/dsh-api-gateway` | yes | Typert Remote Host dispatcher and Client API endpoint |
| `@deepseek-ai/dsh-api-job-controller` | yes | Job Remote observation stream and the reference-counted client job-output service |
| `@deepseek-ai/dsh-api-remotes` | no | Remote BFF assembly for application-selected Host capabilities |
| `@deepseek-ai/dsh-api-session-controller` | yes | Session Remote commands, cold reads, and live control transport |
| `@deepseek-ai/dsh-api-settings-controller` | yes | Remote owner for the configuration surfaces over the settings-domain seams |
| `@deepseek-ai/dsh-api-terminal-controller` | yes | Session-owned interactive terminals with shell discovery, screen recovery and typed Remote control |
| `@deepseek-ai/dsh-api-workspace-controller` | yes | Workspace Remote commands and reconnect-safe state transport |
| `@deepseek-ai/dsh-api-workspace-files` | yes | Workspace file service and Client resource provider: bounded reads, directory listing, and live metadata over the workspaceFiles Remote namespace |
## attachment
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-attachment-local` | yes | Private content-addressed DSH_HOME attachment storage |
## boot
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-config-editor` | no | Persist plugin configuration through profile patches and Loader reconciliation |
| `@deepseek-ai/dsh-hmr` | yes | Coordinated module and profile configuration hot reload |
| `@deepseek-ai/dsh-plugin-manager` | yes | Current-profile plugin and bundle management shared by dsh CLI, Web and agent tools |
## browser-use
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-browser-use` | no | Exclusive named browser-use provider registration |
## bundle
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-acp-app` | no | The dsh ACP profile bundle: automation-only JSON-RPC stdio and process lifecycle over dsh-base |
| `@deepseek-ai/dsh-headless` | yes | The dsh one-shot bundle: a direct core Agent/Session runner over dsh-base with no Host, HTTP, or browser layer |
| `@deepseek-ai/dsh-sdk-app` | yes | The dsh SDK profile bundle: stdio JSON-RPC serving and process lifecycle over dsh-base |
| `@deepseek-ai/dsh-web-app` | yes | The dsh browser-surface bundle: the web patch layer over dsh-base plus the runtime glue plugin (frontend dist serving, web-surface prompt, bash runtime variables, URL line) |
## client
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-client-connection` | yes | Authenticated RPC transport and generation lifecycle |
| `@deepseek-ai/dsh-client-file-upload` | no | Agent-scoped browser file upload, streaming intake, and staged receipt service |
| `@deepseek-ai/dsh-client-hmr` | yes | Web client graph synchronization and rebuilt-bundle reload transport |
| `@deepseek-ai/dsh-client-locale` | no | Locale plugin: Host-backed preference, extensible language catalog, browser fallback, and typed built-in dictionaries |
| `@deepseek-ai/dsh-client-modules` | no | Client module system, dual-face: node half composes the __DSH_BOOT__ entry graph (incremental dsh.client scan, bundle route, index tap, webPlugins service); browser half is the lazy-CJS module table the vendored cordis Loader consumes as its internal seam |
| `@deepseek-ai/dsh-client-resources` | no | Unified client resource model: protocol-registered providers turn URL addresses into live values, consumed through the useResource global standard hook |
| `@deepseek-ai/dsh-client-ui-agent-preset` | no | Agent-preset surfaces: the default for later sessions, this session's seat, and the composition editor |
| `@deepseek-ai/dsh-client-ui-approval` | no | Approval composer takeover over the scoped Remote Event waterfall |
| `@deepseek-ai/dsh-client-ui-attachment` | no | Dynamic attachment presentation plugin for conversation input, message-image, and trajectory image slots |
| `@deepseek-ai/dsh-client-ui-brand-official` | no | Official DeepSeek Harness brand occupants for the Web client's sidebar slots |
| `@deepseek-ai/dsh-client-ui-chat` | no | Chat Conversation target, node definitions, renderers, and details surface |
| `@deepseek-ai/dsh-client-ui-commands` | no | Client command surface: global directory cache, '/' source, three command UI kinds, popupSelect registry |
| `@deepseek-ai/dsh-client-ui-conversation` | no | Target-neutral Conversation assembly, shell, composer, queue, and view navigation |
| `@deepseek-ai/dsh-client-ui-deliverables` | no | Changed-files card with per-file comparison tabs, delivery cards, and clickable final-response file references for Web |
| `@deepseek-ai/dsh-client-ui-directory-picker-browse` | no | In-app directory browsing surface: the workspace directory-flow owner rendering the host's listing and creation primitives |
| `@deepseek-ai/dsh-client-ui-directory-picker-native` | no | Native directory-picker surface: the renderless workspace directory-flow occupant driving the local Desktop or Host OS chooser |
| `@deepseek-ai/dsh-client-ui-goal` | no | Session goal surface: GoalBar docked above the composer, read from the goal session projection |
| `@deepseek-ai/dsh-client-ui-input-trigger` | no | Input trigger pipeline: '/' and '@' detection, candidate menu, pick routing to registered sources |
| `@deepseek-ai/dsh-client-ui-jobs` | no | Session-header background-job list with on-demand streaming record panels |
| `@deepseek-ai/dsh-client-ui-layout` | no | Shell plugin: three-column AppFrame with drag handles, ctx.layout viewing-state service (navigation + panels) |
| `@deepseek-ai/dsh-client-ui-message-feedback` | no | The Web feedback surface: per-message Like/Dislike in the assistant-message action strip and the feedback dialog behind both ratings and /feedback, backed by the messageFeedback and sessionFeedback Host Remotes |
| `@deepseek-ai/dsh-client-ui-model-selection` | no | Model selection over the shared model catalog, Session projection, and session.selectModel |
| `@deepseek-ai/dsh-client-ui-open-in-app` | no | Web "Open In..." controls: the Session-header split button opening the workspace directory in an installed application, and the document preview's default-application controls for one file |
| `@deepseek-ai/dsh-client-ui-permission-presets` | no | Permission surfaces: a new-session default in General settings and a current-session /permission popup over the permissions projection |
| `@deepseek-ai/dsh-client-ui-plan` | no | Plan mode controls, persistent transcript plan cards, and sidebar Markdown previews |
| `@deepseek-ai/dsh-client-ui-plugin-manager` | no | Plugin management for the dsh web client: the sidebar Plugins panel installs, enables, disables, retries, and composes installed plugin packages |
| `@deepseek-ai/dsh-client-ui-reference` | no | Unified Web @file and @session reference source |
| `@deepseek-ai/dsh-client-ui-renderer` | no | Browser UI renderer: React slot bindings, ctx.uiRenderer, and the assembled application root |
| `@deepseek-ai/dsh-client-ui-schedule` | no | Read-only active Schedule catalog in the Web Session header |
| `@deepseek-ai/dsh-client-ui-session` | no | Session Controller adapter for React and session-scoped slots |
| `@deepseek-ai/dsh-client-ui-settings` | no | Settings domain base plugin: shared configuration forms and the canonical settings slot-type contract |
| `@deepseek-ai/dsh-client-ui-settings-account` | yes | Manage DeepSeek login and open Platform billing pages |
| `@deepseek-ai/dsh-client-ui-settings-agent-loop` | no | Settings page of the agent loop on the dsh web client's Plugins page: the parallel tool-call cap of the agent-loop namespace |
| `@deepseek-ai/dsh-client-ui-settings-general` | no | Settings ownerless-copy and product onboarding plugin: the General section, shell trigger/header chrome content, settings dictionaries, and the versioned welcome notice |
| `@deepseek-ai/dsh-client-ui-settings-models` | yes | Models settings and shared product-onboarding dialogs over existing settings and credential joins |
| `@deepseek-ai/dsh-client-ui-settings-plugin-inventory` | no | Read-only Cordis Loader inventory tab in Web Plugins settings |
| `@deepseek-ai/dsh-client-ui-settings-plugins` | no | Built-in plugins settings section for the dsh web client: the Settings navigation entry and the tab chrome feature-owned tabs register into |
| `@deepseek-ai/dsh-client-ui-settings-shell` | no | Settings page of the shell executor on the dsh web client's Plugins page: the command timeout and the per-stream output cap of the shell namespace |
| `@deepseek-ai/dsh-client-ui-settings-subagent` | no | Settings page of Subagent delegation on the dsh web client's Plugins page: recursion depth, parallel capacity, and the models agents may choose for subagents |
| `@deepseek-ai/dsh-client-ui-settings-web-search` | no | Settings page of the DeepSeek web-search provider on the dsh web client's Plugins page: its API key, endpoint, and per-request search budget |
| `@deepseek-ai/dsh-client-ui-sidebar` | no | Sidebar plugin: session multi-level tree, search, grouping, state dots |
| `@deepseek-ai/dsh-client-ui-sidebar-browser` | no | Sandboxed Web browser tabs for the right Sidebar |
| `@deepseek-ai/dsh-client-ui-sidebar-documentpreview` | yes | Extensible Sidebar previews for Office documents, spreadsheets, Markdown, code, images, PDF, HTML, and plain text |
| `@deepseek-ai/dsh-client-ui-sidebar-files` | no | Workspace file tree tab type for the right Sidebar: lazy directory listing over the workspaceFiles Remote namespace, opening files into the Sidebar |
| `@deepseek-ai/dsh-client-ui-sidebar-right` | no | Right Sidebar: the docking surface's session-bound state, its panel and header expand control, and the navigation service over it |
| `@deepseek-ai/dsh-client-ui-sidebar-terminal` | no | Interactive shell tabs for the right Sidebar |
| `@deepseek-ai/dsh-client-ui-skill` | no | Web skill references and the dedicated skill tool row |
| `@deepseek-ai/dsh-client-ui-subagent` | no | Subagent conversation catalog, continuation routing UI, and '@' reference source |
| `@deepseek-ai/dsh-client-ui-theme` | yes | Theme plugin: Host bootstrap for the pre-plugin palette; DOM-free ThemeRuntime for light/dark/system state; --dsw-* token styles and Appearance settings row |
| `@deepseek-ai/dsh-client-ui-tool` | no | Client Tool call-tree renderer and keyed per-tool presentation slot |
| `@deepseek-ai/dsh-client-ui-trajectory` | no | Trajectory event ledger with an interactive timing overview: pure-consumer plugin registering into the conversation ViewMap (no service) |
| `@deepseek-ai/dsh-client-ui-user-questions` | no | Web ask_user_question composer takeover and plan-review presentation UI |
| `@deepseek-ai/dsh-client-ui-workflow-run` | no | Durable workflow-run Conversation Node and nested member disclosure for dsh web |
| `@deepseek-ai/dsh-client-ui-workspace` | no | Workspace picker plugin: one WorkspacePicker registered into the sidebar and empty-state workspace slots |
## compaction
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-command-compact` | no | Human-facing slash command for explicit session compaction |
| `@deepseek-ai/dsh-compaction-basic` | yes | Token-meter-driven compaction policy and LLM summarization backend for the DeepSeek Harness |
| `@deepseek-ai/dsh-compaction-image-offload` | no | Durable image offload for image-capable routes: replace over-budget request images with placeholders and retry |
| `@deepseek-ai/dsh-compaction-tool-result-pruner` | yes | Replay-safe model-free head/middle/tail pruning for tool-result surface nodes |
## computer-use
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-computer-use` | no | Exclusive named computer-use provider registration |
## context
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-agent-instructions` | yes | Workspace context loader for AGENTS.md/CLAUDE.md instruction files |
| `@deepseek-ai/dsh-file-reference-local` | yes | Local-filesystem ctx.fileReferences provider with bounded fuzzy indexes |
| `@deepseek-ai/dsh-session-reference` | yes | Cross-session snapshot references and durable untrusted model context (ctx.sessionReferenceResolver) |
| `@deepseek-ai/dsh-time-context` | yes | Opt-in durable per-step context with the current time and elapsed time |
| `@deepseek-ai/dsh-tmux-context` | yes | Opt-in durable per-step context with this agent's tmux pane and window location |
## core
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-agent` | no | Agent interface, registry, initiator scope, and event vocabulary for the DeepSeek Harness |
| `@deepseek-ai/dsh-agent-default-model` | yes | Default model selection shared by Agent entry points |
| `@deepseek-ai/dsh-agent-loop` | yes | The concrete agent loop plugin for the DeepSeek Harness |
| `@deepseek-ai/dsh-agent-tool-presentation` | yes | Agent-plane presentation selector: composes one agent's tools as PTC mode, native, or both |
| `@deepseek-ai/dsh-session` | no | Event-sourced session store for the DeepSeek Harness |
| `@deepseek-ai/dsh-system-prompt` | yes | System prompt assembly registry for the DeepSeek Harness |
| `@deepseek-ai/dsh-tools` | yes | Tool registry and execution pipeline for the DeepSeek Harness |
## credentials
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-authorization` | no | Authorization seam (ctx.authorization): plugin-owned flows that obtain a credential through a conversation with the human |
| `@deepseek-ai/dsh-credentials-local` | yes | File-backed credentials provider ($DSH_HOME/.env under the live process environment) for the DeepSeek Harness |
| `@deepseek-ai/dsh-deepseek-account-platform` | yes | Authorize DeepSeek accounts through browser PKCE |
## deliverables
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-tool-present` | yes | Explicit workspace file delivery declarations for the DeepSeek Harness |
| `@deepseek-ai/dsh-workspace-changes` | yes | Per-turn workspace file changes recorded from git working-tree snapshots and whole-file captures, with per-file comparisons, for the DeepSeek Harness |
## document
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-office-to-pdf` | yes | Shared Office-to-PDF conversion with bounded queues and caching |
## experimental
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-experimental-agent-team` | yes | Implicit-root Agent Teams roster, durable peer mailbox, and shared task DAG |
| `@deepseek-ai/dsh-experimental-api-speech-to-text` | yes | Authenticated experimental speech transcription for browser clients |
| `@deepseek-ai/dsh-experimental-auto-review` | no | Per-tool LLM authorization review for the DeepSeek Harness Auto permission preset |
| `@deepseek-ai/dsh-experimental-browser-use-chrome-devtools-mcp` | yes | Experimental per-Session Chromium browser tools through chrome-devtools-mcp |
| `@deepseek-ai/dsh-experimental-browser-use-playwright-mcp` | yes | Experimental per-Session Chromium browser tools through @playwright/mcp |
| `@deepseek-ai/dsh-experimental-browser-use-stagehand-native` | yes | Experimental Stagehand browser tools with separately configured native models |
| `@deepseek-ai/dsh-experimental-client-ui-agent-team` | no | Web Agent Teams roster, task board, and teammate navigation |
| `@deepseek-ai/dsh-experimental-client-ui-voice-input` | no | Record speech and insert editable text into the conversation draft |
| `@deepseek-ai/dsh-experimental-computer-use-cua-driver-mcp` | yes | Experimental computer use through an installed Cua Driver MCP executable |
| `@deepseek-ai/dsh-experimental-computer-use-cua-driver-native` | no | Experimental computer-use provider embedding the Cua Driver native npm SDK |
| `@deepseek-ai/dsh-experimental-inspector` | yes | Experimental cross-realm CDP hub for Host debugging and Client Runtime inspection |
| `@deepseek-ai/dsh-experimental-ptc-runtime-python` | yes | CPython subprocess implementation of the DeepSeek Harness PTC execution seam |
| `@deepseek-ai/dsh-experimental-speech-to-text` | yes | Experimental speech recognition with independently selectable providers |
| `@deepseek-ai/dsh-experimental-speech-to-text-sensevoice` | yes | Local SenseVoice ONNX transcription with a managed sherpa-onnx process |
| `@deepseek-ai/dsh-experimental-tool-agent-team` | yes | Scoped model-facing Agent Teams tools over ctx.agentTeams |
## extensions
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-client-ui-cordis` | no | Cordis dynamic-plugin definition card: the keyed cordis_define tool row with its run/stop switch |
| `@deepseek-ai/dsh-cordis-client-runner` | no | Browser half of dynamic dual-half plugin packages: event subscription, closure evaluation, guard facade, and loader entries |
| `@deepseek-ai/dsh-cordis-host-runner` | yes | Dynamic package definition registry, host-half sandbox lifecycle, and invoke handler table for model-mounted dual-half packages |
| `@deepseek-ai/dsh-tool-cordis` | no | Read-only runtime API inspection for Harness plugin development |
## feedback
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-command-feedback` | no | Log-only session feedback: the record event, the sessionFeedback Host Remote, and the human-facing slash command |
| `@deepseek-ai/dsh-message-feedback` | yes | Canonical Session-log ratings and notes for finalized assistant messages |
## fs
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-fs-local` | yes | Local-filesystem implementation of the DeepSeek Harness filesystem seam (ctx.fs) |
| `@deepseek-ai/dsh-fs-observation-policy` | no | File-context policy plugin for the DeepSeek Harness — observed-state, read-before-edit, and version-guarded write/edit added over the ctx.fs provider seam through the fs/* event gate (no service API) |
| `@deepseek-ai/dsh-fs-sandbox` | yes | Sandbox-enforcing implementation of the DeepSeek Harness filesystem seam: fences write/edit by the per-call sandbox mode (read-only denies mutation, workspace-write contains it to the workspace + temp roots) while reads pass through |
| `@deepseek-ai/dsh-tool-fs` | yes | Model-facing filesystem tools (read, write, edit) over the DeepSeek Harness filesystem seam (ctx.fs) |
| `@deepseek-ai/dsh-tool-fs-search` | yes | Model-facing filesystem discovery tools (glob, grep) backed by the packaged ripgrep binary (@vscode/ripgrep) |
| `@deepseek-ai/dsh-tool-str-replace-editor` | yes | Model-facing view, create, literal replace, and line insert tool over the Harness filesystem service |
## goal
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-command-goal` | no | Human-facing slash command for persisted same-session goals |
| `@deepseek-ai/dsh-goal` | yes | Event-sourced same-session goal state and lifecycle service for the DeepSeek Harness |
| `@deepseek-ai/dsh-goal-round-driver` | no | Race-fenced same-session goal-round driver |
| `@deepseek-ai/dsh-tool-goal` | yes | Model-facing same-session goal tools with execution-time authority checks |
## guard
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-repeat-tool-reminder` | yes | Repeat-tool-call guard plugin: advisory reminders when an agent loops on identical tool calls |
| `@deepseek-ai/dsh-tool-call-timeout-policy` | no | Tool-call timeout policy: a tools/execute wrapper that arms a per-tool deadline on exec.signal and returns TOOL_TIMEOUT when it wins |
## hooks
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-hooks-claude-code` | yes | Bridge plugin: run a Claude Code hooks.json / settings hook config on the DeepSeek Harness interception seams |
| `@deepseek-ai/dsh-hooks-codex` | yes | Bridge plugin: run a Codex hooks.json hook config on the DeepSeek Harness interception seams |
## host
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-host-directory-picker-auto` | no | Adaptive chooser of the directory-picker seam: resolves the host situation at boot and mounts the native or browse backend for the DeepSeek Harness web GUI host |
| `@deepseek-ai/dsh-host-directory-picker-browse` | yes | In-app browsing backend of the directory-picker seam (listing/creation primitives over the host filesystem) |
| `@deepseek-ai/dsh-host-directory-picker-native` | no | Native-OS-chooser backend of the directory-picker seam for the DeepSeek Harness web GUI host |
| `@deepseek-ai/dsh-host-frontend-static` | yes | SPA dist server for the Web shell: owns the webserver fallback seat, serving explicit index entries and static assets with traversal rejection and 404 misses |
| `@deepseek-ai/dsh-host-open-in-app` | yes | Host half of open-in-app: resolved application catalog, icons, and the launch endpoint as three webServer routes |
| `@deepseek-ai/dsh-host-plugin-inventory` | no | Read-only Remote projection of current Cordis Loader plugin state |
| `@deepseek-ai/dsh-host-product-telemetry-otel` | yes | Explicit product usage events exported through OpenTelemetry HTTP logs |
| `@deepseek-ai/dsh-host-webserver` | yes | Web route-registration plugin: HTTP and upgrade routes, index transform taps, and static dist fallback; knows no harness concepts |
## interaction
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-commands` | no | Plugin-owned human command registry for DeepSeek Harness UIs |
| `@deepseek-ai/dsh-permission-presets` | yes | User-facing permission presets (ctx.permissionPresets) for the DeepSeek Harness: one product-level Permissions select bundling the sandbox-mode and approval-policy knobs, written through to their own session events |
| `@deepseek-ai/dsh-tool-ask-user` | no | Model-facing ask_user_question tool over the ctx.userQuestions seam |
| `@deepseek-ai/dsh-user-approval` | yes | User-approval seam (ctx.approval) for the DeepSeek Harness: one-shot permission decisions dispatched to composed answerers over the approval/request waterfall, fail-closed by default |
| `@deepseek-ai/dsh-user-questions` | no | Abstract user-questions seam (ctx.userQuestions) for asking the human during agent runs |
## jobs
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-jobs-local` | yes | Process-local implementation of the DeepSeek Harness background job registry seam |
| `@deepseek-ai/dsh-tool-jobs` | yes | Model-facing background job control tools (job_output, job_list, job_kill) over the ctx.jobs registry |
## llm
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-deepseek-llm-api-extensions` | no | Additive request-field registry for the official DeepSeek LLM API adapter |
| `@deepseek-ai/dsh-llm` | no | Provider-neutral LLM service interface for the DeepSeek Harness |
| `@deepseek-ai/dsh-llm-deepseek` | yes | DeepSeek Messages adapter |
| `@deepseek-ai/dsh-llm-pi-ai` | yes | pi-ai-backed DeepSeek adapter for the DeepSeek Harness LLM seam (design-verification twin of dsh-llm-deepseek) |
| `@deepseek-ai/dsh-llm-retry` | yes | Provider-routed LLM request retry policy for the DeepSeek Harness |
| `@deepseek-ai/dsh-plugin-package-inventory-deepseek` | yes | Active Loader-backed plugin package inventory for official DeepSeek LLM API requests |
| `@deepseek-ai/dsh-token-meter` | yes | Replay-aware token measurement service (ctx.tokenMeter) for the DeepSeek Harness |
## lsp
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-lsp` | no | Abstract LSP capability seam (ctx.lsp) for the DeepSeek Harness — language-server provider registry keyed by branded id and extension mapping, order-independent per-query selection, normalized definition/references/implementation/hover requests and results, and the LspError taxonomy |
| `@deepseek-ai/dsh-lsp-stdio` | yes | Generic stdio language-server provider for the DeepSeek Harness LSP capability seam (ctx.lsp) — spawns configured servers, translates JSON-RPC, and serves transient-open goToDefinition/findReferences/goToImplementation/hover queries in the host filesystem namespace |
| `@deepseek-ai/dsh-tool-lsp` | yes | Model-facing lsp tool over the DeepSeek Harness LSP capability seam (ctx.lsp) — one read-only tool with goToDefinition/findReferences/goToImplementation/hover operations, one-based UTF-16 cursor coordinates, bounded location rendering, and hover normalization |
## mcp
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-mcp-client` | yes | MCP client bridge: connects to MCP servers and registers their tools on ctx.tools |
| `@deepseek-ai/dsh-mcp-resources` | no | Scoped MCP resource discovery and reading through shared model tools |
## plan
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-plan-mode` | yes | Logged per-agent plan mode with deployment guidance, a direct slash command, and a user-reviewed exit |
## preset
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-agent-preset` | yes | Declare an Agent capability composition in Cordis YAML |
| `@deepseek-ai/dsh-agent-preset-registry` | yes | Declarative Agent preset registry and profile-backed editing |
| `@deepseek-ai/dsh-persona` | yes | Composition-authored deployment persona section for the DeepSeek Harness |
## ptc-runtime
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-ptc-runtime-node` | yes | Sandboxed Node process implementation of the DeepSeek Harness PTC execution capability |
## runtime-diagnostics
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-invariants` | yes | Registry service for package-owned DeepSeek Harness runtime invariants |
## sandbox
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-sandbox-local` | yes | Local process-sandbox backends for the DeepSeek Harness sandbox seam: bwrap, the npm-distributed landlock-run launcher, macOS Seatbelt, or the Windows ACL restricted-token runner — functionally probed, fail-closed |
| `@deepseek-ai/dsh-sandbox-policy` | yes | Per-call sandbox policy resolver and current model context: deployment fallbacks plus each session's mode and workspace root, shared by every enforcing capability family |
## schedule
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-schedule` | no | Agent-scoped durable after, at, and fixed-rate reminders over the session event log |
## sdk
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-sdk-jsonrpc-server` | yes | Stdio JSON-RPC server plugin for out-of-process DeepSeek Harness SDK clients |
## session
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-session-checkpoint-policy` | no | Semantic session durability checkpoints before model requests and tool side effects |
| `@deepseek-ai/dsh-session-log-deepseek` | yes | Incremental lossless session-log request extension for the official DeepSeek LLM API |
| `@deepseek-ai/dsh-session-persistence-jsonl` | yes | JSONL durable session persistence backend for the DeepSeek Harness |
| `@deepseek-ai/dsh-session-projection` | no | Session-projection seam: the merge-extensible projection type table, the provider contract, and the ctx.sessionProjections registry serving whole current values of log-derived per-session state |
| `@deepseek-ai/dsh-session-projection-cache` | yes | Persisted projection cache (ctx.sessionProjectionCache): durable per-session checkpoint records on the session_projcache storage domain (per-record layout), throttled write-behind, and the cached listing read |
| `@deepseek-ai/dsh-session-stats` | no | Whole-log conversation counts and wall times projection (sessionStats) for the DeepSeek Harness |
| `@deepseek-ai/dsh-session-telemetry-otel` | yes | OpenTelemetry backend for the DeepSeek Harness telemetry seam: hands captured session records to the OTel JS SDK's log pipeline |
| `@deepseek-ai/dsh-session-title` | yes | Log-backed session title service and provider registry for the DeepSeek Harness |
| `@deepseek-ai/dsh-session-title-all-prompts-llm` | yes | All-user-messages LLM provider plugin for DeepSeek Harness session titles |
| `@deepseek-ai/dsh-session-title-first-prompt-llm` | yes | First-message LLM provider plugin for DeepSeek Harness session titles |
| `@deepseek-ai/dsh-session-turn-outline` | no | Whole-log turn outline projection (turnOutline) for the DeepSeek Harness |
## session-query
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-session-log-export` | yes | Web Session-log export command and shared download dialog |
| `@deepseek-ai/dsh-session-query-sqlite` | yes | Concrete ctx.sessionQuery backend with SQLite FTS5 search |
| `@deepseek-ai/dsh-tool-session-query` | yes | Workspace-authorized model-facing session history search, trace, and event read tools |
## settings
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-settings` | no | Abstract user-settings seam (ctx.settings) for the DeepSeek Harness |
## shell
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-bash-local` | yes | Local-subprocess implementation of the DeepSeek Harness bash executor seam |
| `@deepseek-ai/dsh-bash-sandbox` | yes | Sandbox-consuming implementation of the DeepSeek Harness bash executor seam (confines every command via ctx.sandbox, reports denial/enforcement result facts) |
| `@deepseek-ai/dsh-pwsh-local` | yes | Local PowerShell implementation of the DeepSeek Harness bash executor seam |
| `@deepseek-ai/dsh-pwsh-sandbox` | yes | Sandbox-consuming implementation of the DeepSeek Harness PowerShell executor seam (confines every command via ctx.sandbox, reports denial/enforcement result facts) |
| `@deepseek-ai/dsh-shell-env` | yes | Tool-independent managed DSH_* shell environment registry |
| `@deepseek-ai/dsh-tool-bash` | yes | Model-facing bash tool with optional generic background-job and sandbox-escalation support |
| `@deepseek-ai/dsh-tool-bash-persistent` | yes | Model-facing owner-scoped persistent Bash tool backed by the Harness PTY service |
| `@deepseek-ai/dsh-tool-pwsh` | yes | Model-facing pwsh tool over the bash executor seam |
| `@deepseek-ai/dsh-tool-pwsh-persistent` | yes | Model-facing owner-scoped persistent PowerShell tool backed by the Harness PTY service |
## skill
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-skill` | yes | Agent skill provider registry for the DeepSeek Harness |
| `@deepseek-ai/dsh-skill-badge` | no | Bundled dsh badge skill provider for DeepSeek Harness |
| `@deepseek-ai/dsh-skill-filesystem` | yes | Local filesystem skill provider for the DeepSeek Harness |
| `@deepseek-ai/dsh-skill-office` | yes | Bundled Word, PowerPoint, and Excel workflows and structural checks |
| `@deepseek-ai/dsh-tool-skill` | yes | Model-facing skill loading tool for the DeepSeek Harness |
| `@deepseek-ai/dsh-tool-workspace-dependencies` | yes | The load_workspace_dependencies tool: absolute paths into a bundled Python, Node.js, and pnpm payload |
## spill
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-spill-local` | yes | Local-filesystem implementation of the DeepSeek Harness spill storage seam (private session-scoped files) |
| `@deepseek-ai/dsh-spill-policy` | yes | Tool-result spill policy for the DeepSeek Harness — replaces oversized plain-text tool results with a retained preview plus a spill-file path (no service API) |
## ssh
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-fs-ssh` | no | Filesystem provider over the shared POSIX SSH helper |
| `@deepseek-ai/dsh-sandbox-ssh` | no | Remote POSIX sandbox argv provider over the shared SSH helper |
| `@deepseek-ai/dsh-ssh` | yes | Shared OpenSSH connection and versioned POSIX remote helper |
| `@deepseek-ai/dsh-subprocess-ssh` | no | Subprocess and terminal provider over the shared POSIX SSH helper |
## storage
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-storage` | no | Storage hub (ctx.storage): named backend registry plus mounted data-form facilities for the DeepSeek Harness |
| `@deepseek-ai/dsh-storage-domain` | yes | Domain data form (ctx.storage.domain): schema-validated, event-emitting KV domains over storage backends for the DeepSeek Harness |
| `@deepseek-ai/dsh-storage-json` | yes | JSON file KV storage backend for the DeepSeek Harness storage hub |
| `@deepseek-ai/dsh-storage-sqlite` | yes | SQLite storage backend (kv facet) for the DeepSeek Harness storage hub |
## subagent
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-subagent` | yes | Abstract subagent seam (ctx.subagents): named-provider registry for delegating to child agents |
| `@deepseek-ai/dsh-subagent-acp` | yes | Out-of-process ACP subagent backend: drives a child agent in a spawned subprocess over the Agent Client Protocol |
| `@deepseek-ai/dsh-subagent-claude-code` | yes | One-shot Claude Code subagent provider over the official Agent SDK |
| `@deepseek-ai/dsh-subagent-codex` | yes | One-shot Codex subagent provider over the official app-server protocol |
| `@deepseek-ai/dsh-subagent-dsh-sdk` | yes | Out-of-process SDK subagent backend: drives a child DeepSeek Harness runtime subprocess over stdio JSON-RPC through the TypeScript SDK client |
| `@deepseek-ai/dsh-subagent-fork-in-process` | yes | In-process fork subagent backend: runs a child agent seeded with a prefix of the parent's log |
| `@deepseek-ai/dsh-subagent-spawn-in-process` | yes | In-process spawn subagent backend: runs a fresh child agent on ctx.agents |
| `@deepseek-ai/dsh-tool-subagent` | yes | Model-facing subagent delegation tool over the ctx.subagents seam |
| `@deepseek-ai/dsh-tool-subagent-control` | no | Globally named send_message, interrupt_agent, and list_agents tools over ctx.subagents continuations |
## subprocess
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-subprocess-local` | no | Local-subprocess implementation of the DeepSeek Harness subprocess seam |
## terminal
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-terminal` | no | Persistent PTY session seam for the DeepSeek Harness — owner-scoped ids, backend registry, interactive sends, reads, signals, and awaited cleanup |
| `@deepseek-ai/dsh-terminal-bash` | yes | Persistent shell PTY backend over the DeepSeek Harness subprocess terminal primitive |
| `@deepseek-ai/dsh-tool-terminal` | yes | Six model-facing persistent PTY tools with owner isolation and generic background-job integration |
## test-support
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-llm-replay` | yes | Replay LLM plugin: short-circuits llm/stream with model chunks reconstructed from a recorded session JSONL (keyless snapshot tests) |
## todo
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-tool-todo` | yes | Model-facing todo_write tool over the DeepSeek Harness event-sourced session log |
## typert
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-typert-loader` | yes | Loader integration for generated Typert package contributions |
## web
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-tool-web` | yes | Model-facing web tools (web_search, web_fetch) over the DeepSeek Harness web capability seam (ctx.web) |
| `@deepseek-ai/dsh-web` | yes | Abstract web access capability seam (ctx.web) for the DeepSeek Harness — search/fetch provider registry, registration-order-independent selection, request/result vocabulary, and the WebError taxonomy |
| `@deepseek-ai/dsh-web-fetch-http` | yes | Anonymous public HTTP(S) fetch provider for the DeepSeek Harness web capability seam (ctx.web) |
| `@deepseek-ai/dsh-web-search-deepseek` | yes | DeepSeek-backed search provider (native web_search via the Anthropic-compatible API) for the DeepSeek Harness web capability seam (ctx.web) |
| `@deepseek-ai/dsh-web-search-exa` | yes | Exa-backed search provider for the DeepSeek Harness web capability seam (ctx.web) |
| `@deepseek-ai/dsh-web-search-perplexity` | yes | Perplexity-backed search provider for the DeepSeek Harness web capability seam (ctx.web) |
## webhook
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-webhook` | no | Fire-and-forget webhook rule runtime that creates Workspace-backed DeepSeek Harness Sessions |
| `@deepseek-ai/dsh-webhook-github` | yes | Signed GitHub HTTP webhook adapter for the DeepSeek Harness webhook runtime |
## workflow
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-tool-ralph` | yes | Model-facing fresh-agent Ralph loop over the workflow and subagent seams |
| `@deepseek-ai/dsh-tool-workflow` | yes | Model-facing workflow tool: run a JavaScript orchestration script over ctx.workflowEngine |
| `@deepseek-ai/dsh-workflow-ptc` | yes | Workflow orchestration in the shared sandboxed Node PTC runtime |
## workspace
| Package | Config | Description |
|---|---|---|
| `@deepseek-ai/dsh-workspace` | no | Workspace entity registry (ctx.workspaceRegistry): durable workspace records with validated session attachment over the domain data form for the DeepSeek Harness |
@@ -5,104 +5,33 @@ description: Use when authoring, installing, configuring, or debugging persisten
# Persistent Harness plugins
Use ordinary workspace files to author a bundle, then `plugin_manager install_bundle` to install it in the current profile. Changes affect every session in that profile and survive restart. Load `editing-cordis-compositions` for agent preset changes.
Use ordinary workspace files to author a bundle, then `plugin_manager` with `action: install_bundle` and the absolute package directory as `target` to install it in the current profile. Changes affect every session in that profile and survive restart. Load `editing-cordis-compositions` for agent preset changes.
## Deliver a working plugin first
1. Resolve the requested result and destination. An unspecified visual destination is the current Harness Web UI; a standalone image or HTML file does not complete such a request. Choose reasonable visual details and implement a small first version; install it before visual refinement.
2. Discover only the APIs needed for that version: `cordis_inspect_list`, then targeted `cordis_inspect_query` calls. For UI, query Client `Slots.listSubTree` and the selected slot's registration options and props. Treat the recipes below and returned API declarations as the supported implementation path. Once the chosen slot and registration API are known, write the plugin. Before the first installation, resolve missing declarations through inspection; do not re-check these recipes by reading Loader, manifest-parser, package-manager, React, or slot implementation source. Source-level diagnosis starts from a concrete installation or runtime failure.
3. The first files you write are the installable package, patch, and required Host/Client files in one workspace directory. Check JavaScript syntax and the manifest, then install it. Before that first installation, do not create preview HTML, mock shells, design variants, screenshot scripts, or rasterizer tooling. Use the installed plugin itself as the first preview.
2. Discover only the APIs needed for that version: `cordis_inspect_list`, then targeted `cordis_inspect_query` calls. For UI, query Client `Slots.listSubTree` and the selected slot's registration options and props. Once the chosen slot and registration API are known, write the plugin.
3. Read the matching template under `templates/` with the file-read tool and write its copies into one workspace directory, or write the installable package, patch, and required Host/Client files there yourself. Check JavaScript syntax and the manifest, then install it. Before that first installation, do not create preview HTML, mock shells, design variants, screenshot scripts, or rasterizer tooling. Use the installed plugin itself as the first preview.
4. Read the installation result. After `application: applied`, exercise the capability or inspect the live Client registration. Use the connected page for visual verification when browser control is available. State any verification limitation explicitly; installation and slot registration alone do not establish what the user can see.
5. Fix observed defects in the same plugin. When the requested result works, finish with its location and verification status. Do not continue speculative visual variants, optional features, or a new mock preview. Close any task list you created.
## Package and install
## Knowledge sources, in order
A bundle declares `dsh.bundle.patch` in `package.json`. Its YAML patch inserts plugin entries. Give the package and rows unique names; use the Loader's existing YAML syntax, including `!!js` where expressions are needed. Read an existing patch before editing it: a matching override replaces the complete config.
1. Inspection: `cordis_inspect_query` answers exact Service methods and Event modes (`Service`, `Event`), a mounted plugin's Config JSON Schema (`Config.listConfigs`: filter the paged directory by `name`, then query the `entry` id), the Tools this Agent can call (`Tool`), and live Client Slots and theme tokens (`Slots`, `Theme`).
2. Package documentation: `Config.listConfigs` with `name` set to the package finds its entries; querying one `entry` returns its `packageDir`, the resolved package directory. Read `<packageDir>/README.md`. Bundled packages resolve from the dsh installation and profile-installed bundles from the profile, so never guess the path from `$DSH_PROFILE_DIR`.
3. Source: installed packages ship built `lib/index.js` and `lib/types/**/*.d.ts` with JSDoc under that same `packageDir`, not `src/`; a source checkout of DSH has `packages/<group>/<name>/src`. Read them when inspection and the README leave a question open, and start source-level diagnosis from a concrete installation or runtime failure.
For a simple drawing, prefer a slot with allocated space, such as `conversation.composer.dock` when available. Keep the first version within that slot’s flow; do not plan a motion path around host controls. This minimal package needs no dependencies, install scripts, or build tool:
`DSH_PROFILE` (profile name) and `DSH_PROFILE_DIR` (its directory, whose `node_modules` holds only profile-installed bundles) are set in every shell call of a profile-launched Harness and absent when the Harness was booted without a profile. Bash reads them as `$DSH_PROFILE`; PowerShell, which the Windows preset uses, reads them as `$env:DSH_PROFILE`. With `dsh` on the PATH, `dsh --profile "$DSH_PROFILE" --dump-config` prints the composed profile.
```json
{
"name": "@local/my-decoration",
"version": "1.0.0",
"private": true,
"type": "module",
"exports": { ".": "./index.js", "./client": "./client.js" },
"dsh": {
"bundle": { "patch": "./cordis.patch.yml" },
"client": {
"platform": "web",
"immediately": true,
"inject": ["@deepseek-ai/dsh-client-ui-conversation"]
}
}
}
```
## Read next
`index.js` exports `export function apply() {}`. A Host plugin with behavior exports either a service class as default or named `apply`, `inject`, and optional `Config`; do not mix these forms. The bundle's `cordis.patch.yml`:
The files below live in this skill's base directory, which the `skill` tool reported. Read them with the file-read tool only: in Desktop that directory sits inside `app.asar`, which the Host process can open but shell commands (`cat`, `cp`, `ls`), `node`, and pnpm cannot. Never install or syntax-check a template in place; copy its contents into the workspace first.
```yaml
- insert:
- id: my-decoration
name: '@local/my-decoration'
```
Call `plugin_manager` with `action: install_bundle` and the absolute package directory as `target`. It performs package installation and bundle selection; do not reproduce those steps with shell commands. Only pass `approvedBuilds` after the user explicitly approves the reported pending build scripts. Preserve returned failures and pending states; report success only after observing the requested capability.
Use `list_plugins` or `list_bundles` to obtain exact identifiers for existing installations. `set_plugin` and `set_bundle` toggle them; `remove_bundle` removes a bundle. Inspect saved-state and activation outcomes separately: `failed` requires diagnosis, `overridden` means a higher-priority layer wins, and `restart-required` means the change is not live. Installing a new bundle can activate through HMR; replacing an installed package requires restart to load a fresh JavaScript module generation. Do not infer updated browser code from an unchanged slot id.
## Client implementation
The browser artifact registers a lazy factory whose id equals the package name. React comes from the browser module table; no duplicate React installation, CDN script, or UMD search is needed. For compiled sources, use the deployment's Client build tooling to emit this format; declare non-baseline runtime imports in `dsh.client.external`.
This `client.js` example uses `conversation.composer.dock` for a small drawing below the composer. Replace the artwork with the requested drawing. Follow the selected slot's props and options. Do not read another plugin's DOM, stylesheet, or component source to estimate placement; choose a slot that already allocates space. Use `shell.overlay` only when the request needs an overlay and its placement is known.
```js
window.__ModuleLoader__.load({
id: '@local/my-decoration',
factory(require) {
const React = require('react');
const h = React.createElement;
function Decoration() {
return h('svg', {
viewBox: '0 0 64 64', width: 48, height: 48,
'aria-hidden': true,
style: { display: 'block', pointerEvents: 'none' },
}, h('circle', { cx: 32, cy: 32, r: 24, fill: '#247bbf' }));
}
return {
inject: ['slots'],
apply(ctx) {
ctx.slots.inject('conversation.composer.dock', () => ctx.slots.register({
name: 'conversation.composer.dock', id: 'my-decoration', order: 5,
}, Decoration));
},
};
},
});
```
Keep factories free of side effects. Register styles, timers, listeners and other resources inside `apply` with `ctx.effect`/`ctx.on` and return their cleanup functions. Component-local styles can render as React elements so unmounting removes them. Verify disposal for resources you add. Inherit the host theme for containers and controls; artwork may use its own colors. Route visible UI text through the Client locale service. Do not replace the app root or append a second application to `document.body`.
## Verify in the available environment
Prefer the authenticated page already connected to Harness. Do not launch a separate browser from shell commands, change `HOME`, inspect personal browser profiles, search for authentication tokens, or alter keychains to obtain a screenshot. For a visual-only request without browser control, limit verification to JavaScript syntax, manifest validation, and the live Client slot, then report that visual verification remains unavailable. Before or after installation, do not search for rasterizers, invoke Quick Look, extract SVG into preview files, emulate React/DOM, or implement a custom renderer to compensate for missing browser control. A screenshot of a mock page is not verification of the running plugin.
For any test subprocess or temporary resource, use a unique owned directory, bound execution, and await cleanup. A failed optional preview must not turn into environment repair or block installation.
## Connect an MCP server
Create a configuration-only bundle: its manifest needs a unique name, version, and `dsh.bundle.patch`, but no Host/Client entry files. Insert the already installed MCP client in its patch:
```yaml
- insert:
- id: demo-mcp
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: demo
transport: streamable-http
url: http://127.0.0.1:3000/mcp
failOnStartupError: true
```
Replace the endpoint, install the bundle through `plugin_manager`, then call `mcp__demo__ping` or another newly available `mcp__<serverName>__<tool>` tool to verify the connection. For stdio, use `transport: stdio`, `command`, and optional `args`, `env`, and `cwd`. Ambient credentials are scrubbed; reference existing credentials with Loader `!!js` rather than copying secrets into conversation text. Repair the same bundle on failure instead of creating duplicates.
| Task | File |
|---|---|
| Bundle manifest, display metadata and icon, install and enable semantics, Host plugin export forms, Config | `references/host-plugin.md` |
| A UI plugin rendered in the Web page: Client manifest, module loader, slot registration | `references/ui-plugin.md` |
| Connecting an MCP server through a configuration-only bundle | `references/mcp-bundle.md` |
| Verification limits when no browser control is available | `references/verification.md` |
| Starting points to read and copy into the workspace | `templates/decoration/` (UI plugin), `templates/mcp/` (MCP bundle) |
| Loader patch dialect and the list of installable plugin packages | the `cordis-composition-reference` skill |
@@ -0,0 +1,60 @@
# Bundles and Host plugins
A bundle is a package whose `package.json` declares `dsh.bundle.patch`; the YAML patch inserts plugin entries. Give the package and rows unique names; use the Loader's existing YAML syntax, including `!!js` where expressions are needed. Read an existing patch before editing it: a matching override replaces the complete `config`.
## Manifest
A Host-only bundle needs no dependencies, install scripts, or build tool:
```json
{
"name": "@local/my-plugin",
"version": "1.0.0",
"private": true,
"type": "module",
"exports": { ".": "./index.js" },
"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }
}
```
`cordis.patch.yml`:
```yaml
- insert:
- id: my-plugin
name: '@local/my-plugin'
config: {}
```
## Display metadata and icon
Plugin Manager cards, bundle details, component rows, and the Settings plugin inventory read display text and an icon from the manifest without activating the plugin. Put the title and description in `locale/en.json` (other languages such as `locale/zh.json` use the same fields), and declare the icon as a top-level `icon` in `package.json`:
```json
{ "meta": { "title": "My Decoration", "description": "Draws a badge under the composer." } }
```
```json
{
"icon": "./icon.svg",
"exports": { "./package.json": "./package.json", "./locale/*.json": "./locale/*.json" },
"files": ["locale/*.json", "icon.svg"]
}
```
`icon` is a path relative to the manifest directory; SVG, PNG, JPEG, and WebP up to 256 KiB are accepted, while absolute paths, URLs, paths outside the directory, and symlinks leaving it are rejected. Missing fields fall back to `package.json` `name` and `description` and to the panel's default artwork; malformed metadata produces a diagnostic and keeps the valid text.
## Host plugin export forms
`index.js` exports one of these forms; do not mix them:
- `export function apply(ctx, config) {}` with optional `export const inject = ['tools']` and `export const Config`.
- A service class as the default export.
Register every resource inside `apply` with `ctx.effect` or `ctx.on` and return its cleanup. A plugin that declares `Config` validates the row's `config` at activation; query `Config.listConfigs` for an installed plugin's schema before writing its `config`, and follow `$defs` references in the returned document.
## Install, enable, and observe
`plugin_manager` `install_bundle` performs package installation and bundle selection; do not reproduce those steps with shell commands. Only pass `approvedBuilds` after the user explicitly approves the reported pending build scripts. Preserve returned failures and pending states; report success only after observing the requested capability.
`list_plugins` and `list_bundles` return exact identifiers for existing installations. `set_plugin` and `set_bundle` toggle them; `remove_bundle` removes a bundle. Inspect saved-state and activation outcomes separately: `failed` requires diagnosis, `overridden` means a higher-priority layer wins, and `restart-required` means the change is not live. Installing a new bundle can activate through HMR; replacing an installed package requires restart to load a fresh JavaScript module generation. Do not infer updated browser code from an unchanged slot id.
@@ -0,0 +1,5 @@
# Connect an MCP server
Read `templates/mcp/` with the file-read tool and write its files into your bundle directory. A configuration-only bundle's manifest needs a unique name, version, and `dsh.bundle.patch`, but no Host/Client entry files. Its patch, `templates/mcp/cordis.patch.yml`, inserts the already installed `@deepseek-ai/dsh-mcp-client` with `serverName`, `transport: streamable-http`, `url`, and `failOnStartupError: true`.
Replace the endpoint, install the bundle through `plugin_manager`, then call `mcp__demo__ping` or another newly available `mcp__<serverName>__<tool>` tool to verify the connection. For stdio, use `transport: stdio`, `command`, and optional `args`, `env`, and `cwd`; `Config.listConfigs` on the installed row returns the complete client schema. Ambient credentials are scrubbed; reference existing credentials with Loader `!!js` rather than copying secrets into conversation text. Repair the same bundle on failure instead of creating duplicates.
@@ -0,0 +1,13 @@
# UI plugins in the Web page
Read `templates/decoration/` with the file-read tool and write its files into your bundle directory: its `package.json` adds a `dsh.client` section (`platform`, `immediately`, `inject`) and a `./client` export beside the bundle patch.
`index.js` exports `export function apply() {}`; the patch inserts one row named after the package. For a simple drawing, prefer a slot with allocated space, such as `conversation.composer.dock` when available. Keep the first version within that slot's flow; do not plan a motion path around host controls. Use `shell.overlay` only when the request needs an overlay and its placement is known.
## Client module
The browser artifact registers a lazy factory whose id equals the package name. React comes from the browser module table; no duplicate React installation, CDN script, or UMD search is needed. For compiled sources, use the deployment's Client build tooling to emit this format; declare non-baseline runtime imports in `dsh.client.external`.
`templates/decoration/client.js` registers a lazy factory into `conversation.composer.dock` through `ctx.slots.inject` and `ctx.slots.register`; follow the selected slot's props and options from `Slots.listSubTree` when you change the slot.
Keep factories free of side effects. Register styles, timers, listeners and other resources inside `apply` with `ctx.effect`/`ctx.on` and return their cleanup functions. Component-local styles can render as React elements so unmounting removes them. Verify disposal for resources you add. Inherit the host theme for containers and controls; artwork may use its own colors. Route visible UI text through the Client locale service. Do not replace the app root or append a second application to `document.body`. Do not read another plugin's DOM, stylesheet, or component source to estimate placement; choose a slot that already allocates space.
@@ -0,0 +1,5 @@
# Verify in the available environment
Prefer the authenticated page already connected to Harness. Do not launch a separate browser from shell commands, change `HOME`, inspect personal browser profiles, search for authentication tokens, or alter keychains to obtain a screenshot. For a visual-only request without browser control, limit verification to JavaScript syntax, manifest validation, and the live Client slot, then report that visual verification remains unavailable. Before or after installation, do not search for rasterizers, invoke Quick Look, extract SVG into preview files, emulate React/DOM, or implement a custom renderer to compensate for missing browser control. A screenshot of a mock page is not verification of the running plugin.
For any test subprocess or temporary resource, use a unique owned directory, bound execution, and await cleanup. A failed optional preview must not turn into environment repair or block installation.
@@ -0,0 +1,22 @@
window.__ModuleLoader__.load({
id: '@local/my-decoration',
factory(require) {
const React = require('react');
const h = React.createElement;
function Decoration() {
return h('svg', {
viewBox: '0 0 64 64', width: 48, height: 48,
'aria-hidden': true,
style: { display: 'block', pointerEvents: 'none' },
}, h('circle', { cx: 32, cy: 32, r: 24, fill: '#247bbf' }));
}
return {
inject: ['slots'],
apply(ctx) {
ctx.slots.inject('conversation.composer.dock', () => ctx.slots.register({
name: 'conversation.composer.dock', id: 'my-decoration', order: 5,
}, Decoration));
},
};
},
});
@@ -0,0 +1,3 @@
- insert:
- id: my-decoration
name: '@local/my-decoration'
@@ -0,0 +1,2 @@
/** Host half of the decoration bundle; the Client module owns the rendering. */
export function apply() {}
@@ -0,0 +1,15 @@
{
"name": "@local/my-decoration",
"version": "1.0.0",
"private": true,
"type": "module",
"exports": { ".": "./index.js", "./client": "./client.js" },
"dsh": {
"bundle": { "patch": "./cordis.patch.yml" },
"client": {
"platform": "web",
"immediately": true,
"inject": ["@deepseek-ai/dsh-client-ui-conversation"]
}
}
}
@@ -0,0 +1,8 @@
- insert:
- id: demo-mcp
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: demo
transport: streamable-http
url: http://127.0.0.1:3000/mcp
failOnStartupError: true
@@ -0,0 +1,7 @@
{
"name": "@local/demo-mcp",
"version": "1.0.0",
"private": true,
"type": "module",
"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }
}
@@ -5,11 +5,11 @@ description: Use when creating, changing, or validating an agent preset or other
# Editing Cordis compositions
Agent presets are ordinary `@deepseek-ai/dsh-agent-preset` declarations carried by bundle patches. Nothing edits a declaration in place: a preset is created or changed by installing a bundle whose patch declares or overrides it. This file is the complete contract; do not read DSH package sources to infer formats.
Agent presets are ordinary `@deepseek-ai/dsh-agent-preset` declarations carried by bundle patches. Nothing edits a declaration in place: a preset is created or changed by installing a bundle whose patch declares or overrides it. This file states the declaration format; when it leaves a question open, read the `@deepseek-ai/dsh-agent-preset` README and `lib/types` declarations under the `packageDir` that `cordis_inspect_query` `Config.listConfigs` returns when queried with a `preset-<id>` row's `entry` id, or `packages/preset/agent-preset/src` in a source checkout.
## Where declarations live
The shipped Web presets are `presets/<id>.patch.yml` files of the `@deepseek-ai/dsh-web-app` bundle, ids `standard`, `ptc`, `minimal` and `cordis`. Installed, that bundle sits at `$DSH_HOME/profiles/<profile>/node_modules/@deepseek-ai/dsh-web-app/`; in a source checkout of DSH it is `packages/bundle/web-app/`. Read one file with `cat` when you need a template; `minimal.patch.yml` is the shortest.
The shipped Web presets are `presets/<id>.patch.yml` files of the `@deepseek-ai/dsh-web-app` bundle, ids `standard`, `ptc`, `minimal` and `cordis`. Installed, the bundle resolves from the dsh installation, not the profile; querying `Config.listConfigs` with the `entry` id of any `preset-<id>` row it declares returns that `packageDir`. In a source checkout of DSH it is `packages/bundle/web-app/`. Read one file with the file-read tool when you need a template; `minimal.patch.yml` is the shortest. In Desktop the bundle sits inside `app.asar`, which shell commands cannot open. Load `cordis-composition-reference` for the patch dialect and the list of plugin packages a preset can mount.
A declaration row has these `config` fields: `id` (required, lowercase letters, digits and hyphens), `plugins` (required Cordis entry list), and optional `name`, `description` and `order` (roster position). The Loader row `id` is `preset-<id>` by convention.
@@ -65,6 +65,10 @@ Override the declaration by its Loader row id instead of inserting. The override
# the shipped list with your changes
```
## Migrate a legacy preset
Before declaration rows, a user preset was a directory `$DSH_HOME/.agent-presets/<id>/` holding `preset.yml` (display `name`, `description`, `order`) and `agent.cordis.yml` (the plugin entry list). Nothing reads that directory any more. To migrate one, create a bundle as above whose declaration takes `id` from the directory name, `name`, `description`, and `order` from `preset.yml`, and `plugins` from `agent.cordis.yml` verbatim; check each plugin name against `cordis-composition-reference` because packages renamed since the preset was written fail at activation. Install it, verify the row, then delete the legacy directory.
## Verify
`plugin_manager` `list_bundles` lists the installed bundle; `list_plugins` shows the `preset-<id>` row with its activation state. A declaration whose activation fails stays on the roster with its diagnostic and cannot compose a session until the bundle is fixed and reinstalled. Existing sessions and their children keep the plugin revision they started with; validate changed behavior in a new session. Installing a bundle executes plugin code in the Host process, so it requires Full access or approval.
@@ -77,4 +81,4 @@ A preset plugin that supplies a service must isolate the provider and all its co
## Extend the Host
For new plugin code or profile-wide capabilities, load `cordis-plugin-development`, author a workspace bundle, and install it with `plugin_manager`. Inspect available APIs through `cordis_inspect_list` and `cordis_inspect_query`; those tools do not invoke Remote methods. Verify the installed capability before reporting completion.
For new plugin code or profile-wide capabilities, load `cordis-plugin-development`, author a workspace bundle, and install it with `plugin_manager`. Inspect available APIs through `cordis_inspect_list` and `cordis_inspect_query`, including a mounted plugin's Config schema through the Host `Config` provider; those tools do not invoke Remote methods. Verify the installed capability before reporting completion.
@@ -0,0 +1,72 @@
/**
* Shipped creator skills: every rendered skill stays below the pruner threshold, referenced
* files exist, templates parse, and no skill bans reading DSH sources.
*/
import { execFileSync } from 'node:child_process'
import { readdirSync, readFileSync, statSync } from 'node:fs'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import * as yaml from 'js-yaml'
import { entryListSchema } from '@deepseek-ai/cordis-plugin-include'
import { codePointLength } from '@deepseek-ai/dsh-compaction-tool-result-pruner'
import { renderSkillContent } from '@deepseek-ai/dsh-skill'
import { describe, expect, it } from 'vitest'
const skills = fileURLToPath(new URL('../skills/', import.meta.url))
const names = readdirSync(skills)
/**
* `packages/bundle/web-app/presets/standard.patch.yml` prunes a tool result only above `thresholdChars: 8192`
* code points; a skill result below it reaches the model intact.
*/
const PRUNER_THRESHOLD_CHARS = 8192
/** The body the `skill` tool returns: the file without its YAML frontmatter. */
function body(name: string): string {
const text = readFileSync(join(skills, name, 'SKILL.md'), 'utf8')
const end = text.indexOf('\n---\n', 4)
return text.slice(end + '\n---\n'.length)
}
function markdownFiles(dir: string): string[] {
return readdirSync(dir, { recursive: true, encoding: 'utf8' })
.filter(file => file.endsWith('.md'))
.map(file => join(dir, file))
}
describe('the shipped creator skills', () => {
it('render below the pruner threshold and name only files that exist', () => {
for (const name of names) {
const rendered = renderSkillContent({
name, provider: 'filesystem', resourceBase: { kind: 'directory', path: join(skills, name) }, content: body(name),
})
expect(codePointLength(rendered), name).toBeLessThan(PRUNER_THRESHOLD_CHARS)
const referenced = [...body(name).matchAll(/`((?:references|templates)\/[^`]+)`/g)].map(match => match[1] ?? match[0])
for (const path of referenced) expect(() => statSync(join(skills, name, path)), `${name}: ${path}`).not.toThrow()
}
})
it('ship templates whose manifest, patch, and JavaScript parse', () => {
const templates = join(skills, 'cordis-plugin-development', 'templates')
for (const name of readdirSync(templates)) {
const dir = join(templates, name)
const manifest = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) as { dsh: { bundle: { patch: string } } }
const patch = yaml.load(readFileSync(join(dir, manifest.dsh.bundle.patch), 'utf8'), { schema: entryListSchema })
expect(Array.isArray(patch)).toBe(true)
for (const file of readdirSync(dir).filter(entry => entry.endsWith('.js'))) {
execFileSync(process.execPath, ['--check', join(dir, file)])
}
}
})
it('never forbid reading DSH package sources and never route skill files through the shell', () => {
for (const name of names) {
for (const file of markdownFiles(join(skills, name))) {
const text = readFileSync(file, 'utf8')
expect(text, file).not.toMatch(/do not read (DSH |package |DSH package )?sources/i)
// Desktop ships the skill directory inside app.asar, which only the Host process can open.
expect(text, file).not.toMatch(/`(cat|cp|ls) /)
}
}
})
})
+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/shell/shell-env/README.md
README.md: f85fb0005e94dc59939cfd2e9f0d69e5cdc7d133
README.zh.md: b9f763a247fc792335326d8945dfeb2db1690fc7
README.md: 6557049c8287348507e2f5fc8a824366949c2861
README.zh.md: 32bd5e744af98ca84e426490765d15dd1701851c
+5 -5
View File
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
## Summary
`dsh-shell-env` provides the trusted `DSH_*` environment that every model shell call — bash or pwsh — runs with: built-in facts such as `DSH_HOME`, `DSH_SHELL=1`, and the agent's `DSH_SESSION_ID`. Plugin authors can register their own facts with declared keys, collected per execution and disposed with their plugin; duplicate ownership or undeclared runtime keys fail loudly instead of silently overwriting. The registry changes nothing else the model sees — the shell tools own their own schemas and prompts. Choose it in any composition that mounts a model shell tool; configuration only picks the Harness home directory.
`dsh-shell-env` provides the trusted `DSH_*` environment that every model shell call — bash or pwsh — runs with: built-in facts such as `DSH_HOME`, `DSH_SHELL=1`, the agent's `DSH_SESSION_ID`, and the launched profile's `DSH_PROFILE` and `DSH_PROFILE_DIR`. Plugin authors can register their own facts with declared keys, collected per execution and disposed with their plugin; duplicate ownership or undeclared runtime keys fail loudly instead of silently overwriting. The registry changes nothing else the model sees — the shell tools own their own schemas and prompts. Choose it in any composition that mounts a model shell tool; configuration only picks the Harness home directory.
## Table of Contents
@@ -29,7 +29,7 @@ Load this plugin in any composition that mounts a model shell tool (`dsh-tool-ba
### What every shell call receives
Every call receives `DSH_HOME` (the absolute Harness home), `DSH_SHELL=1`, and, for agent calls, `DSH_SESSION_ID` (the calling session's id).
Every call receives `DSH_HOME` (the absolute Harness home), `DSH_SHELL=1`, and, for agent calls, `DSH_SESSION_ID` (the calling session's id). When the launcher provided a profile context, every call also receives `DSH_PROFILE` (the profile name) and `DSH_PROFILE_DIR` (its absolute directory; its `node_modules` holds only profile-installed packages, while the harness's own bundles resolve from the dsh installation); compositions booted without a profile omit both.
### Adding your own environment facts
@@ -64,7 +64,7 @@ The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-a
### What can go wrong
Two contributors declaring the same key, or a contributor claiming a reserved built-in (`DSH_HOME`, `DSH_SHELL`, `DSH_SESSION_ID`), fails plugin load loudly. A `DSH_*` key must be all-caps with underscores (for example `DSH_REGION`), and a missing description fails registration.
Two contributors declaring the same key, or a contributor claiming a reserved built-in (`DSH_HOME`, `DSH_SHELL`, `DSH_SESSION_ID`, `DSH_PROFILE`, `DSH_PROFILE_DIR`), fails plugin load loudly. A `DSH_*` key must be all-caps with underscores (for example `DSH_REGION`), and a missing description fails registration.
-----
@@ -80,7 +80,7 @@ This section explains the design decisions behind the registry and points at the
- **Trusted namespace, rebuilt per call.** The environment is a Harness-owned `DSH_*` namespace: the shell executor discards inherited `DSH_*` values and merges the registry's current snapshot for each execution, so nested harnesses and concurrent parent/child agents cannot leak stale identities, and `process.env` is never modified.
- **Declared ownership, loud conflicts.** Contributors declare their keys up front so duplicate ownership is detected before the first command; resolvers may only return declared keys.
- **Built-ins stay here.** `DSH_HOME`, `DSH_SHELL`, and `DSH_SESSION_ID` are reserved for the registry; contributors cannot claim them.
- **Built-ins stay here.** `DSH_HOME`, `DSH_SHELL`, `DSH_SESSION_ID`, `DSH_PROFILE`, and `DSH_PROFILE_DIR` are reserved for the registry; contributors cannot claim them.
### Source map
@@ -127,7 +127,7 @@ The managed environment never enters the request prefix, so it does not invalida
These limits define when the registry is a poor fit or needs care. They are current package constraints, not a task backlog.
- **`list()` enumerates plugin-contributed variables only** — registry-owned built-ins (`DSH_HOME`, `DSH_SHELL`, `DSH_SESSION_ID`) are not included, so diagnostics, prompt, or UI code must not treat `list()` as an exhaustive environment catalog.
- **`list()` enumerates plugin-contributed variables only** — registry-owned built-ins (`DSH_HOME`, `DSH_SHELL`, `DSH_SESSION_ID`, `DSH_PROFILE`, `DSH_PROFILE_DIR`) are not included, so diagnostics, prompt, or UI code must not treat `list()` as an exhaustive environment catalog.
<a id="dev-note"></a>
### Dev Note
+5 -5
View File
@@ -9,7 +9,7 @@ kind: "package-reference"
## 概述
`dsh-shell-env` 提供每次模型 shell 调用——bash 或 pwsh——所运行的受信 `DSH_*` 环境:内置事实如 `DSH_HOME`、`DSH_SHELL=1` 与 agent(智能体)的 `DSH_SESSION_ID`。插件作者可以注册自己的事实,带声明键、按每次执行收集,并随插件释放;重复所有权或未声明的运行时键会明确报错,而不是静默覆盖。注册表不会改变模型看到的其他任何内容——shell 工具拥有各自的 schema 与提示词。任何挂载了模型 shell 工具的组合都适合选择它;配置只决定 Harness 主目录。
`dsh-shell-env` 提供每次模型 shell 调用——bash 或 pwsh——所运行的受信 `DSH_*` 环境:内置事实如 `DSH_HOME`、`DSH_SHELL=1`、agent(智能体)的 `DSH_SESSION_ID`,以及所启动 profile 的 `DSH_PROFILE` 与 `DSH_PROFILE_DIR`。插件作者可以注册自己的事实,带声明键、按每次执行收集,并随插件释放;重复所有权或未声明的运行时键会明确报错,而不是静默覆盖。注册表不会改变模型看到的其他任何内容——shell 工具拥有各自的 schema 与提示词。任何挂载了模型 shell 工具的组合都适合选择它;配置只决定 Harness 主目录。
## 目录
@@ -29,7 +29,7 @@ kind: "package-reference"
### 每次 shell 调用都会收到什么
每次调用都会收到 `DSH_HOME`(Harness 主目录的绝对路径)、`DSH_SHELL=1`,agent 调用还会收到 `DSH_SESSION_ID`(调用方会话的 id)。
每次调用都会收到 `DSH_HOME`(Harness 主目录的绝对路径)、`DSH_SHELL=1`,agent 调用还会收到 `DSH_SESSION_ID`(调用方会话的 id)。启动器提供了 profile 上下文时,每次调用还会收到 `DSH_PROFILE`(profile 名)与 `DSH_PROFILE_DIR`(其绝对目录;其 `node_modules` 只存放 profile 自行安装的包,harness 自带的组合包从 dsh 安装目录解析);不带 profile 启动的组合则两者都没有。
### 添加你自己的环境事实
@@ -64,7 +64,7 @@ contributor 必须声明它返回的每个键;返回未声明或非字符串
### 可能出什么问题
两个 contributor 声明同一个键,或 contributor 声称拥有保留内置键(`DSH_HOME`、`DSH_SHELL`、`DSH_SESSION_ID`),都会导致插件加载时明确报错。`DSH_*` 键必须全大写并带下划线(例如 `DSH_REGION`),缺少描述也会让注册失败。
两个 contributor 声明同一个键,或 contributor 声称拥有保留内置键(`DSH_HOME`、`DSH_SHELL`、`DSH_SESSION_ID`、`DSH_PROFILE`、`DSH_PROFILE_DIR`),都会导致插件加载时明确报错。`DSH_*` 键必须全大写并带下划线(例如 `DSH_REGION`),缺少描述也会让注册失败。
-----
@@ -80,7 +80,7 @@ contributor 必须声明它返回的每个键;返回未声明或非字符串
- **受信命名空间,每次调用重建。** 环境是归 Harness 所有的 `DSH_*` 命名空间:shell 执行器丢弃继承的 `DSH_*` 值,并为每次执行合并注册表的当前快照,因此嵌套 harness 与并发的父子 agent 无法泄漏陈旧身份,`process.env` 也永不被修改。
- **声明所有权,冲突明确报错。** contributor 预先声明键,使重复所有权在第一条命令之前就被发现;resolver 只能返回已声明的键。
- **内置键留在这里。** `DSH_HOME`、`DSH_SHELL` 与 `DSH_SESSION_ID` 为注册表保留;contributor 不能声称拥有它们。
- **内置键留在这里。** `DSH_HOME`、`DSH_SHELL`、`DSH_SESSION_ID`、`DSH_PROFILE` 与 `DSH_PROFILE_DIR` 为注册表保留;contributor 不能声称拥有它们。
### 源码地图
@@ -127,7 +127,7 @@ contributor 必须声明它返回的每个键;返回未声明或非字符串
这些限制说明注册表何时不合适或需要小心使用。它们是当前包约束,不是任务积压。
- **`list()` 只枚举插件贡献的变量**——注册表自有的内置键(`DSH_HOME`、`DSH_SHELL`、`DSH_SESSION_ID`)不包含在内,因此诊断、提示词或 UI 代码不得把 `list()` 当作完整的环境目录。
- **`list()` 只枚举插件贡献的变量**——注册表自有的内置键(`DSH_HOME`、`DSH_SHELL`、`DSH_SESSION_ID`、`DSH_PROFILE`、`DSH_PROFILE_DIR`)不包含在内,因此诊断、提示词或 UI 代码不得把 `list()` 当作完整的环境目录。
<a id="dev-note"></a>
### 开发备注
+9 -7
View File
@@ -27,20 +27,22 @@
],
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-shell": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-app-boot": "workspace:^",
"@deepseek-ai/dsh-home-paths": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
"@deepseek-ai/dsh-shell": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^"
},
"dependencies": {
"@deepseek-ai/schemastery": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-shell": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-app-boot": "workspace:^",
"@deepseek-ai/dsh-home-paths": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-shell": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^"
}
}
+11
View File
@@ -14,6 +14,8 @@ import { DSH_ENV_PREFIX } from '@deepseek-ai/dsh-shell'
import type { DshEnvironment, DshEnvironmentKey } from '@deepseek-ai/dsh-shell'
import { DSH_HOME_ENV, resolveDshHome } from '@deepseek-ai/dsh-home-paths'
import type { ToolExecution } from '@deepseek-ai/dsh-tools'
// Declares `Context.profileContext`, the launcher-provided profile the built-ins read.
import type {} from '@deepseek-ai/dsh-app-boot'
declare module '@deepseek-ai/cordis' {
interface Context {
@@ -69,10 +71,14 @@ export interface BashEnvVariableInfo extends BashEnvVariable {
const DSH_SHELL_KEY = `${DSH_ENV_PREFIX}SHELL` as const
const DSH_SESSION_ID_KEY = `${DSH_ENV_PREFIX}SESSION_ID` as const
const DSH_PROFILE_KEY = `${DSH_ENV_PREFIX}PROFILE` as const
const DSH_PROFILE_DIR_KEY = `${DSH_ENV_PREFIX}PROFILE_DIR` as const
const RESERVED_BASH_ENV_KEYS = new Set<DshEnvironmentKey>([
DSH_HOME_ENV,
DSH_SHELL_KEY,
DSH_SESSION_ID_KEY,
DSH_PROFILE_KEY,
DSH_PROFILE_DIR_KEY,
])
const BASH_ENV_KEY_SUFFIX = /^[A-Z][A-Z0-9_]*$/
@@ -155,6 +161,11 @@ export class ShellEnvRegistry extends Service {
if (execution.agent !== undefined) {
values[DSH_SESSION_ID_KEY] = execution.agent.session.header.id
}
const profile = this.ctx.get('profileContext')
if (profile !== undefined) {
values[DSH_PROFILE_KEY] = profile.name
values[DSH_PROFILE_DIR_KEY] = profile.dir
}
for (const contributor of [...this.contributors.values()].sort((left, right) => left.name.localeCompare(right.name))) {
const resolved = contributor.resolve(execution)
@@ -55,6 +55,21 @@ describe('ShellEnvRegistry', () => {
})
})
it('collects the launcher-provided profile name and directory when a profile context exists', () => {
const ctx = new Context()
ctx.provide('profileContext', {
name: 'web', dir: '/profiles/web', patchPath: '/profiles/web/cordis.patch.yml', installAnchor: '/dsh/package.json',
cwd: '/work', home: '/home', startedBundles: [], overlays: [], telemetryDisabledEnv: undefined,
})
const registry = new ShellEnvRegistry(ctx, { dshHome: './test-dsh-home' })
expect(registry.collect(execution())).toMatchObject({ DSH_PROFILE: 'web', DSH_PROFILE_DIR: '/profiles/web' })
expect(() => registry.register({
name: 'profile-claimer',
variables: { DSH_PROFILE: { description: 'Reserved key.' } },
resolve: () => ({}),
})).toThrow(/reserved key "DSH_PROFILE"/)
})
it('resolves DSH_HOME from the ambient override or the user-home default', () => {
vi.stubEnv('DSH_HOME', './ambient-dsh-home')
const fromEnvironment = new ShellEnvRegistry(new Context())
+3
View File
@@ -25,6 +25,9 @@
},
{
"path": "../../core/tools"
},
{
"path": "../../boot/app-boot"
}
]
}
+27
View File
@@ -7820,12 +7820,18 @@ importers:
'@deepseek-ai/cordis':
specifier: workspace:^
version: link:../../../vendor/cordis
'@deepseek-ai/cordis-plugin-group':
specifier: workspace:^
version: link:../../../vendor/group
'@deepseek-ai/cordis-plugin-loader':
specifier: workspace:^
version: link:../../../vendor/loader
'@deepseek-ai/dsh-agent':
specifier: workspace:^
version: link:../../core/agent
'@deepseek-ai/dsh-app-boot':
specifier: workspace:^
version: link:../../boot/app-boot
'@deepseek-ai/dsh-cordis-host-runner':
specifier: workspace:^
version: link:../cordis-host-runner
@@ -7835,6 +7841,9 @@ importers:
'@deepseek-ai/dsh-tools':
specifier: workspace:^
version: link:../../core/tools
'@deepseek-ai/schemastery':
specifier: link:../../../vendor/schemastery
version: link:../../../vendor/schemastery
packages/extensions/ui-cordis:
devDependencies:
@@ -9545,12 +9554,27 @@ importers:
'@deepseek-ai/cordis':
specifier: workspace:^
version: link:../../../vendor/cordis
'@deepseek-ai/cordis-plugin-include':
specifier: workspace:^
version: link:../../../vendor/include
'@deepseek-ai/cordis-plugin-loader':
specifier: workspace:^
version: link:../../../vendor/loader
'@deepseek-ai/dsh-agent-preset-registry':
specifier: workspace:^
version: link:../agent-preset-registry
'@deepseek-ai/dsh-compaction-tool-result-pruner':
specifier: workspace:^
version: link:../../compaction/compaction-tool-result-pruner
'@deepseek-ai/dsh-skill':
specifier: workspace:^
version: link:../../skill/skill
'@types/js-yaml':
specifier: ^4.0.9
version: 4.0.9
js-yaml:
specifier: ^4.2.0
version: 4.3.1
packages/preset/agent-preset-registry:
dependencies:
@@ -10875,6 +10899,9 @@ importers:
'@deepseek-ai/dsh-agent':
specifier: workspace:^
version: link:../../core/agent
'@deepseek-ai/dsh-app-boot':
specifier: workspace:^
version: link:../../boot/app-boot
'@deepseek-ai/dsh-home-paths':
specifier: workspace:^
version: link:../../util/home-paths
+83
View File
@@ -0,0 +1,83 @@
/**
* Generate the loadable-plugin package list shipped inside the
* `cordis-composition-reference` skill from the workspace manifests and the
* config-catalog classification. `--check` exits 1 when the committed file is stale.
*/
import { readFileSync, writeFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { collectConfigCatalog, type CatalogEntry } from './gen-config-catalog.ts'
const root = resolve(import.meta.dirname, '..')
const OUT = 'packages/preset/agent-preset/skills/cordis-composition-reference/references/packages.md'
interface Row {
pkg: string
group: string
config: boolean
description: string
}
function rows(entries: readonly CatalogEntry[]): Row[] {
const result: Row[] = []
for (const entry of entries) {
// Seams are abstract Service Definitions and libraries export no plugin; neither is a Loader row.
if (entry.kind !== 'config' && entry.kind !== 'no-config') continue
const manifest = JSON.parse(readFileSync(resolve(root, entry.dir, 'package.json'), 'utf8')) as { description: string }
// Catalog dirs are `packages/<group>/<pkg>`, so the group segment is always present.
const group = entry.dir.split('/')[1] ?? entry.dir
result.push({ pkg: entry.pkg, group, config: entry.kind === 'config', description: manifest.description })
}
return result.sort((left, right) => left.group.localeCompare(right.group) || left.pkg.localeCompare(right.pkg))
}
/**
* Render the package list as one Markdown document grouped by package group.
* @param entries - config-catalog entries for every workspace package.
* @returns the complete file content.
*/
export function render(entries: readonly CatalogEntry[]): string {
const lines = [
'# Loadable Harness plugin packages',
'',
'This file is GENERATED from workspace manifests (`scripts/gen-plugin-packages.ts`) and verified fresh by `pnpm run verify-plugin-packages` (part of `doc-sync`); do not edit it by hand.',
'',
'Every package below exports a Cordis plugin that a bundle patch can name in a Loader row. `Config` marks packages whose row accepts a `config` mapping; query `Config.listConfigs` through `cordis_inspect_query` (filter by `name`, then query the `entry` id) for the mounted schema. Packages under `experimental` are pre-stable.',
'',
]
let group = ''
for (const row of rows(entries)) {
if (row.group !== group) {
if (group !== '') lines.push('')
group = row.group
lines.push(`## ${group}`, '', '| Package | Config | Description |', '|---|---|---|')
}
lines.push(`| \`${row.pkg}\` | ${row.config ? 'yes' : 'no'} | ${row.description} |`)
}
return `${lines.join('\n')}\n`
}
function main(): void {
const content = render(collectConfigCatalog())
if (process.argv.includes('--check')) {
let committed: string | null
try {
committed = readFileSync(resolve(root, OUT), 'utf8')
} catch {
// A missing or unreadable file has the same remedy as a stale one: regenerate.
committed = null
}
if (committed === content) {
console.log(`gen-plugin-packages: ${OUT} is up to date.`)
process.exit(0)
}
console.error(`gen-plugin-packages: ${OUT} is stale. Run \`pnpm run gen-plugin-packages\` and commit ${OUT}.`)
process.exit(1)
}
writeFileSync(resolve(root, OUT), content)
console.log(`gen-plugin-packages: wrote ${OUT}.`)
}
if (process.argv[1] && import.meta.filename === resolve(process.argv[1])) {
main()
}
-1
View File
@@ -1332,7 +1332,6 @@
},
"packages/extensions/tool-cordis/src/providers.ts": {
"1b3ab83a5e026a9147a8e49324b677b78af1f2983acde975fe791b29ff755f40": 1,
"2efdc6a912b71e4b7c561b1e7ba6975dab2927589c8b1f1af65806410d065a1e": 1,
"3706ba0e7ecb37ef9aa4d5e6bb732001b78443acf77f015f97b1b11d3a2cf941": 1,
"67c8811e65c7d4c40d6de95cb9b9576873c717164d609124dcac39e82e95abca": 1
},
+1
View File
@@ -797,6 +797,7 @@ function docSyncLeafGates(options: {
pnpmScript('export-jsdoc', 'verify-export-jsdoc', { label: 'export jsdoc' }),
pnpmScript('tool-catalog', 'verify-tool-catalog', { label: 'tool catalog' }),
pnpmScript('config-catalog', 'verify-config-catalog', { label: 'config catalog' }),
pnpmScript('plugin-packages', 'verify-plugin-packages', { label: 'skill plugin package list' }),
pnpmScript('dependency-catalog', 'verify-dependency-catalog', { label: 'npm dependency catalog', quick: true }),
pnpmScript('persistence-catalog', 'verify-persistence-catalog', { label: 'persistence catalog' }),
pnpmScript('persistence-changes', 'verify-persistence-changes', { label: 'persistence type history' }),
@@ -0,0 +1,18 @@
{"type":"session","version":4,"id":"{{session:2}}","createdAt":1783950001000,"cwd":"{{cwd}}","parentSession":"{{session:1}}","isSeeded":false,"origin":"subagent","delegationDepth":1}
{"type":"sandbox/mode","data":{"mode":"danger-full-access","source":"delegation"}}
{"type":"approval/policy","data":{"policy":"never","source":"delegation"}}
{"type":"permission/preset","data":{"preset":"danger-full-access"}}
{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Reply with exactly DIRECT_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"{{message:13}}"}]}}
{"type":"turn/start","data":{"turn":1}}
{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
{"type":"subagent/descriptor","data":{"version":3,"mode":"one-shot","provider":"spawn","label":"Check direct child"}}
{"type":"step/start","data":{"turn":1,"step":1}}
{"type":"system/message","data":{"turn":1,"step":1,"message":{"role":"system","content":[{"type":"text","text":"{{system}}"}],"source":{"kind":"system-prompt"},"id":"{{message:14}}"}},"surfaceOp":"append"}
{"type":"user/message","data":{"content":[{"type":"text","text":"Reply with exactly DIRECT_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"{{message:13}}"},"surfaceOp":"append"}
{"type":"user/message","data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`).\n\nYou are a delegated subagent: your permission scope was fixed when you were started and cannot be widened from inside this session — operations that require approval are rejected automatically. When the task needs access beyond that scope, do not retry the denied operation; state the limitation in your reply so the delegating agent can handle it."}],"source":{"kind":"runtime-context","form":"snapshot","sections":[{"name":"sandbox:policy","text":"Current DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations."},{"name":"approval:policy","text":"Approval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."},{"name":"subagent:delegation","text":"You are a delegated subagent: your permission scope was fixed when you were started and cannot be widened from inside this session — operations that require approval are rejected automatically. When the task needs access beyond that scope, do not retry the denied operation; state the limitation in your reply so the delegating agent can handle it."}]},"role":"user","id":"{{message:15}}"},"surfaceOp":"append"}
{"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"session/title","data":{"title":"Reply with exactly DIRECT_CHILD_OK and","messageSeqs":[9],"source":{"kind":"fallback"}}}
{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"DIRECT_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:16}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1788699344566,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1788699344566,"index":0,"dt":[],"texts":["DIRECT_CHILD_OK"]},{"type":"chunk","time":1788699344566,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DIRECT_CHILD_OK"}}},{"type":"chunk","time":1788699344566,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1788699344566,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"}
{"type":"step/end","data":{"turn":1,"step":1}}
{"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}}
@@ -0,0 +1,18 @@
{"type":"session","version":4,"id":"{{session:3}}","createdAt":1783950002000,"cwd":"{{cwd}}","parentSession":"{{session:1}}","isSeeded":false,"origin":"subagent","delegationDepth":1}
{"type":"sandbox/mode","data":{"mode":"danger-full-access","source":"delegation"}}
{"type":"approval/policy","data":{"policy":"never","source":"delegation"}}
{"type":"permission/preset","data":{"preset":"danger-full-access"}}
{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Reply with exactly WORKFLOW_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"{{message:17}}"}]}}
{"type":"turn/start","data":{"turn":1}}
{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
{"type":"subagent/descriptor","data":{"version":3,"mode":"one-shot","provider":"spawn"}}
{"type":"step/start","data":{"turn":1,"step":1}}
{"type":"system/message","data":{"turn":1,"step":1,"message":{"role":"system","content":[{"type":"text","text":"{{system}}"}],"source":{"kind":"system-prompt"},"id":"{{message:18}}"}},"surfaceOp":"append"}
{"type":"user/message","data":{"content":[{"type":"text","text":"Reply with exactly WORKFLOW_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"{{message:17}}"},"surfaceOp":"append"}
{"type":"user/message","data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`).\n\nYou are a delegated subagent: your permission scope was fixed when you were started and cannot be widened from inside this session — operations that require approval are rejected automatically. When the task needs access beyond that scope, do not retry the denied operation; state the limitation in your reply so the delegating agent can handle it."}],"source":{"kind":"runtime-context","form":"snapshot","sections":[{"name":"sandbox:policy","text":"Current DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations."},{"name":"approval:policy","text":"Approval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."},{"name":"subagent:delegation","text":"You are a delegated subagent: your permission scope was fixed when you were started and cannot be widened from inside this session — operations that require approval are rejected automatically. When the task needs access beyond that scope, do not retry the denied operation; state the limitation in your reply so the delegating agent can handle it."}]},"role":"user","id":"{{message:19}}"},"surfaceOp":"append"}
{"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"session/title","data":{"title":"Reply with exactly WORKFLOW_CHILD_OK and","messageSeqs":[9],"source":{"kind":"fallback"}}}
{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"WORKFLOW_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:20}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1788699344748,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1788699344748,"index":0,"dt":[],"texts":["WORKFLOW_CHILD_OK"]},{"type":"chunk","time":1788699344748,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"WORKFLOW_CHILD_OK"}}},{"type":"chunk","time":1788699344748,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1788699344748,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"}
{"type":"step/end","data":{"turn":1,"step":1}}
{"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}}
@@ -0,0 +1,43 @@
{"type":"session","version":4,"id":"{{session:1}}","createdAt":1783950000000,"cwd":"{{cwd}}","isSeeded":false,"delegationDepth":0}
{"type":"permission/preset","data":{"preset":"danger-full-access"}}
{"type":"sandbox/mode","data":{"mode":"danger-full-access"}}
{"type":"approval/policy","data":{"policy":"never"}}
{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Run this advanced flow exactly once: list runtime inspect providers; discover providers through run_code when available; delegate once to a direct spawn child; run one workflow that delegates to another spawn child; list providers again; then reply with exactly ADVANCED_ACP_OK."}],"source":{"kind":"user"},"role":"user","id":"{{message:1}}"}]}}
{"type":"turn/start","data":{"turn":1}}
{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
{"type":"step/start","data":{"turn":1,"step":1}}
{"type":"system/message","data":{"turn":1,"step":1,"message":{"role":"system","content":[{"type":"text","text":"{{system}}"}],"source":{"kind":"system-prompt"},"id":"{{message:2}}"}},"surfaceOp":"append"}
{"type":"user/message","data":{"content":[{"type":"text","text":"Run this advanced flow exactly once: list runtime inspect providers; discover providers through run_code when available; delegate once to a direct spawn child; run one workflow that delegates to another spawn child; list providers again; then reply with exactly ADVANCED_ACP_OK."}],"source":{"kind":"user"},"role":"user","id":"{{message:1}}"},"surfaceOp":"append"}
{"type":"user/message","data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"runtime-context","form":"snapshot","sections":[{"name":"sandbox:policy","text":"Current DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations."},{"name":"approval:policy","text":"Approval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}]},"role":"user","id":"{{message:3}}"},"surfaceOp":"append"}
{"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"session/title","data":{"title":"Run this advanced flow exactly","messageSeqs":[8],"source":{"kind":"fallback"}}}
{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-inspect-before","name":"cordis_inspect_list","arguments":"{}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1788880370600,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1788880370600,"index":0,"dt":[],"id":"advanced-inspect-before","name":"cordis_inspect_list","args":["{}"]},{"type":"chunk","time":1788880370600,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-inspect-before","name":"cordis_inspect_list","arguments":"{}"}}},{"type":"chunk","time":1788880370600,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1788880370600,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}
{"type":"tool/call","data":{"turn":1,"step":1,"callId":"advanced-inspect-before","name":"cordis_inspect_list","arguments":"{}"}}
{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"advanced-inspect-before"},"toolCallId":"advanced-inspect-before","content":[{"type":"text","text":"{\n \"providers\": [\n {\n \"platform\": \"host\",\n \"id\": \"Service\",\n \"description\": \"Progressive Host Service discovery: compact capability/signature directory, then one exact coding contract.\",\n \"methods\": [\n {\n \"name\": \"listService\",\n \"description\": \"Progressive Host Service discovery: compact capability/signature directory, then one exact coding contract.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"service\": {\n \"type\": \"string\",\n \"description\": \"Exact Service key. Omit it for the compact Service and method-signature directory.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"Compact Service directory, or one exact Service contract with only its referenced type declarations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Event\",\n \"description\": \"Progressive Host Event discovery: compact listener directory, then one exact event contract.\",\n \"methods\": [\n {\n \"name\": \"listEvents\",\n \"description\": \"Progressive Host Event discovery: compact listener directory, then one exact event contract.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"event\": {\n \"type\": \"string\",\n \"description\": \"Exact Event name. Omit it for the compact Event and listener-signature directory.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"Compact Event directory, or one exact Event contract with only its referenced type declarations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Config\",\n \"description\": \"Progressive live plugin Config discovery: paged entry directory with schema status, then one entry's exact JSON Schema.\",\n \"methods\": [\n {\n \"name\": \"listConfigs\",\n \"description\": \"Progressive live plugin Config discovery: paged entry directory with schema status, then one entry's exact JSON Schema.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"entry\": {\n \"type\": \"string\",\n \"description\": \"Exact Loader entry id from the directory; returns that entry's projected Config schema.\"\n },\n \"name\": {\n \"type\": \"string\",\n \"description\": \"Exact plugin package name; limits the directory to its entries.\"\n },\n \"offset\": {\n \"type\": \"number\",\n \"description\": \"Zero-based directory offset; defaults to 0.\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Directory page size, from 1 to 100; defaults to 25.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"One directory page of live entries with patch ids, Config status, total, and nextOffset, or one entry's projected JSON Schema with shared definitions, omission acceptance, and projection limitations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Tool\",\n \"description\": \"Tools visible to the requesting Agent, including scoped and dynamic registrations.\",\n \"methods\": [\n {\n \"name\": \"listTools\",\n \"description\": \"Return every Tool schema currently callable by this Agent.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {},\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"JSON data owned by this inspect provider.\"\n }\n }\n ]\n }\n ]\n}"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"}
{"type":"step/end","data":{"turn":1,"step":1}}
{"type":"step/start","data":{"turn":1,"step":2}}
{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-direct-child","name":"subagent","arguments":"{\"description\":\"Check direct child\",\"prompt\":\"Reply with exactly DIRECT_CHILD_OK and nothing else.\",\"run_in_background\":false}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1788880370619,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1788880370619,"index":0,"dt":[],"id":"advanced-direct-child","name":"subagent","args":["{\"description\":\"Check direct child\",\"prompt\":\"Reply with exactly DIRECT_CHILD_OK and nothing else.\",\"run_in_background\":false}"]},{"type":"chunk","time":1788880370619,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-direct-child","name":"subagent","arguments":"{\"description\":\"Check direct child\",\"prompt\":\"Reply with exactly DIRECT_CHILD_OK and nothing else.\",\"run_in_background\":false}"}}},{"type":"chunk","time":1788880370619,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1788880370619,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}
{"type":"tool/call","data":{"turn":1,"step":2,"callId":"advanced-direct-child","name":"subagent","arguments":"{\"description\":\"Check direct child\",\"prompt\":\"Reply with exactly DIRECT_CHILD_OK and nothing else.\",\"run_in_background\":false}"}}
{"type":"subagent/catalog","data":{"version":0,"childId":"{{session:2}}","childCreatedAt":1788880370624,"mode":"one-shot","label":"Check direct child"}}
{"type":"tool/result","data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"advanced-direct-child"},"toolCallId":"advanced-direct-child","content":[{"type":"text","text":"DIRECT_CHILD_OK"}],"isError":false,"id":"{{message:7}}"}},"sourceEventSeqs":[19],"surfaceOp":"append"}
{"type":"step/end","data":{"turn":1,"step":2}}
{"type":"step/start","data":{"turn":1,"step":3}}
{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-workflow","name":"workflow","arguments":"{\"script\":\"phase('Delegate')\\nconst reply = await agent('Reply with exactly WORKFLOW_CHILD_OK and nothing else.', { label: 'workflow-child' })\\nreturn { reply }\",\"meta\":{\"name\":\"advanced-acp-snapshot\",\"description\":\"exercise one workflow child through ACP\"}}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1788880370690,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1788880370690,"index":0,"dt":[],"id":"advanced-workflow","name":"workflow","args":["{\"script\":\"phase('Delegate')\\nconst reply = await agent('Reply with exactly WORKFLOW_CHILD_OK and nothing else.', { label: 'workflow-child' })\\nreturn { reply }\",\"meta\":{\"name\":\"advanced-acp-snapshot\",\"description\":\"exercise one workflow child through ACP\"}}"]},{"type":"chunk","time":1788880370690,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-workflow","name":"workflow","arguments":"{\"script\":\"phase('Delegate')\\nconst reply = await agent('Reply with exactly WORKFLOW_CHILD_OK and nothing else.', { label: 'workflow-child' })\\nreturn { reply }\",\"meta\":{\"name\":\"advanced-acp-snapshot\",\"description\":\"exercise one workflow child through ACP\"}}"}}},{"type":"chunk","time":1788880370690,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1788880370690,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}
{"type":"tool/call","data":{"turn":1,"step":3,"callId":"advanced-workflow","name":"workflow","arguments":"{\"script\":\"phase('Delegate')\\nconst reply = await agent('Reply with exactly WORKFLOW_CHILD_OK and nothing else.', { label: 'workflow-child' })\\nreturn { reply }\",\"meta\":{\"name\":\"advanced-acp-snapshot\",\"description\":\"exercise one workflow child through ACP\"}}"}}
{"type":"tool-workflow/run-start","data":{"runId":"{{workflow:1}}","name":"advanced-acp-snapshot"}}
{"type":"subagent/catalog","data":{"version":0,"childId":"{{session:3}}","childCreatedAt":1788880370792,"mode":"one-shot"}}
{"type":"tool-workflow/agent-start","data":{"runId":"{{workflow:1}}","seq":1,"label":"workflow-child","phase":"Delegate","childId":"{{session:3}}"}}
{"type":"tool-workflow/agent-end","data":{"runId":"{{workflow:1}}","seq":1,"outcome":"completed"}}
{"type":"tool-workflow/run-end","data":{"runId":"{{workflow:1}}","stopReason":"completed"}}
{"type":"tool/result","data":{"turn":1,"step":3,"message":{"role":"tool","source":{"kind":"tool","callId":"advanced-workflow"},"toolCallId":"advanced-workflow","content":[{"type":"text","text":"workflow \"advanced-acp-snapshot\" completed (1 agent).\nReturn value:\n{\n \"reply\": \"WORKFLOW_CHILD_OK\"\n}"}],"isError":false,"id":"{{message:9}}"}},"sourceEventSeqs":[25],"surfaceOp":"append"}
{"type":"step/end","data":{"turn":1,"step":3}}
{"type":"step/start","data":{"turn":1,"step":4}}
{"type":"assistant/message","data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-inspect-after","name":"cordis_inspect_list","arguments":"{}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:10}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1788880370853,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1788880370853,"index":0,"dt":[],"id":"advanced-inspect-after","name":"cordis_inspect_list","args":["{}"]},{"type":"chunk","time":1788880370853,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-inspect-after","name":"cordis_inspect_list","arguments":"{}"}}},{"type":"chunk","time":1788880370853,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1788880370853,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}
{"type":"tool/call","data":{"turn":1,"step":4,"callId":"advanced-inspect-after","name":"cordis_inspect_list","arguments":"{}"}}
{"type":"tool/result","data":{"turn":1,"step":4,"message":{"role":"tool","source":{"kind":"tool","callId":"advanced-inspect-after"},"toolCallId":"advanced-inspect-after","content":[{"type":"text","text":"{\n \"providers\": [\n {\n \"platform\": \"host\",\n \"id\": \"Service\",\n \"description\": \"Progressive Host Service discovery: compact capability/signature directory, then one exact coding contract.\",\n \"methods\": [\n {\n \"name\": \"listService\",\n \"description\": \"Progressive Host Service discovery: compact capability/signature directory, then one exact coding contract.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"service\": {\n \"type\": \"string\",\n \"description\": \"Exact Service key. Omit it for the compact Service and method-signature directory.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"Compact Service directory, or one exact Service contract with only its referenced type declarations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Event\",\n \"description\": \"Progressive Host Event discovery: compact listener directory, then one exact event contract.\",\n \"methods\": [\n {\n \"name\": \"listEvents\",\n \"description\": \"Progressive Host Event discovery: compact listener directory, then one exact event contract.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"event\": {\n \"type\": \"string\",\n \"description\": \"Exact Event name. Omit it for the compact Event and listener-signature directory.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"Compact Event directory, or one exact Event contract with only its referenced type declarations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Config\",\n \"description\": \"Progressive live plugin Config discovery: paged entry directory with schema status, then one entry's exact JSON Schema.\",\n \"methods\": [\n {\n \"name\": \"listConfigs\",\n \"description\": \"Progressive live plugin Config discovery: paged entry directory with schema status, then one entry's exact JSON Schema.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"entry\": {\n \"type\": \"string\",\n \"description\": \"Exact Loader entry id from the directory; returns that entry's projected Config schema.\"\n },\n \"name\": {\n \"type\": \"string\",\n \"description\": \"Exact plugin package name; limits the directory to its entries.\"\n },\n \"offset\": {\n \"type\": \"number\",\n \"description\": \"Zero-based directory offset; defaults to 0.\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Directory page size, from 1 to 100; defaults to 25.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"One directory page of live entries with patch ids, Config status, total, and nextOffset, or one entry's projected JSON Schema with shared definitions, omission acceptance, and projection limitations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Tool\",\n \"description\": \"Tools visible to the requesting Agent, including scoped and dynamic registrations.\",\n \"methods\": [\n {\n \"name\": \"listTools\",\n \"description\": \"Return every Tool schema currently callable by this Agent.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {},\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"JSON data owned by this inspect provider.\"\n }\n }\n ]\n }\n ]\n}"}],"isError":false,"id":"{{message:11}}"}},"sourceEventSeqs":[35],"surfaceOp":"append"}
{"type":"step/end","data":{"turn":1,"step":4}}
{"type":"step/start","data":{"turn":1,"step":5}}
{"type":"assistant/message","data":{"turn":1,"step":5,"message":{"role":"assistant","content":[{"type":"text","text":"ADVANCED_ACP_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:12}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1788880370871,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1788880370871,"index":0,"dt":[],"texts":["ADVANCED_ACP_OK"]},{"type":"chunk","time":1788880370871,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"ADVANCED_ACP_OK"}}},{"type":"chunk","time":1788880370871,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1788880370871,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"}
{"type":"step/end","data":{"turn":1,"step":5}}
{"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}}
@@ -0,0 +1,18 @@
{"type":"session","version":4,"id":"{{session:2}}","createdAt":1783950001000,"cwd":"{{cwd}}","parentSession":"{{session:1}}","isSeeded":false,"origin":"subagent","delegationDepth":1}
{"type":"sandbox/mode","data":{"mode":"danger-full-access","source":"delegation"}}
{"type":"approval/policy","data":{"policy":"never","source":"delegation"}}
{"type":"permission/preset","data":{"preset":"danger-full-access"}}
{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Reply with exactly DIRECT_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"{{message:15}}"}]}}
{"type":"turn/start","data":{"turn":1}}
{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
{"type":"subagent/descriptor","data":{"version":3,"mode":"one-shot","provider":"spawn","label":"Check direct child"}}
{"type":"step/start","data":{"turn":1,"step":1}}
{"type":"system/message","data":{"turn":1,"step":1,"message":{"role":"system","content":[{"type":"text","text":"{{system}}"}],"source":{"kind":"system-prompt"},"id":"{{message:16}}"}},"surfaceOp":"append"}
{"type":"user/message","data":{"content":[{"type":"text","text":"Reply with exactly DIRECT_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"{{message:15}}"},"surfaceOp":"append"}
{"type":"user/message","data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`).\n\nYou are a delegated subagent: your permission scope was fixed when you were started and cannot be widened from inside this session — operations that require approval are rejected automatically. When the task needs access beyond that scope, do not retry the denied operation; state the limitation in your reply so the delegating agent can handle it."}],"source":{"kind":"runtime-context","form":"snapshot","sections":[{"name":"sandbox:policy","text":"Current DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations."},{"name":"approval:policy","text":"Approval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."},{"name":"subagent:delegation","text":"You are a delegated subagent: your permission scope was fixed when you were started and cannot be widened from inside this session — operations that require approval are rejected automatically. When the task needs access beyond that scope, do not retry the denied operation; state the limitation in your reply so the delegating agent can handle it."}]},"role":"user","id":"{{message:17}}"},"surfaceOp":"append"}
{"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"session/title","data":{"title":"Reply with exactly DIRECT_CHILD_OK and","messageSeqs":[9],"source":{"kind":"fallback"}}}
{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"DIRECT_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:18}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1788699342933,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1788699342933,"index":0,"dt":[],"texts":["DIRECT_CHILD_OK"]},{"type":"chunk","time":1788699342933,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"DIRECT_CHILD_OK"}}},{"type":"chunk","time":1788699342933,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1788699342933,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"}
{"type":"step/end","data":{"turn":1,"step":1}}
{"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}}
@@ -0,0 +1,18 @@
{"type":"session","version":4,"id":"{{session:3}}","createdAt":1783950002000,"cwd":"{{cwd}}","parentSession":"{{session:1}}","isSeeded":false,"origin":"subagent","delegationDepth":1}
{"type":"sandbox/mode","data":{"mode":"danger-full-access","source":"delegation"}}
{"type":"approval/policy","data":{"policy":"never","source":"delegation"}}
{"type":"permission/preset","data":{"preset":"danger-full-access"}}
{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Reply with exactly WORKFLOW_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"{{message:19}}"}]}}
{"type":"turn/start","data":{"turn":1}}
{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
{"type":"subagent/descriptor","data":{"version":3,"mode":"one-shot","provider":"spawn"}}
{"type":"step/start","data":{"turn":1,"step":1}}
{"type":"system/message","data":{"turn":1,"step":1,"message":{"role":"system","content":[{"type":"text","text":"{{system}}"}],"source":{"kind":"system-prompt"},"id":"{{message:20}}"}},"surfaceOp":"append"}
{"type":"user/message","data":{"content":[{"type":"text","text":"Reply with exactly WORKFLOW_CHILD_OK and nothing else."}],"source":{"kind":"user"},"role":"user","id":"{{message:19}}"},"surfaceOp":"append"}
{"type":"user/message","data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`).\n\nYou are a delegated subagent: your permission scope was fixed when you were started and cannot be widened from inside this session — operations that require approval are rejected automatically. When the task needs access beyond that scope, do not retry the denied operation; state the limitation in your reply so the delegating agent can handle it."}],"source":{"kind":"runtime-context","form":"snapshot","sections":[{"name":"sandbox:policy","text":"Current DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations."},{"name":"approval:policy","text":"Approval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."},{"name":"subagent:delegation","text":"You are a delegated subagent: your permission scope was fixed when you were started and cannot be widened from inside this session — operations that require approval are rejected automatically. When the task needs access beyond that scope, do not retry the denied operation; state the limitation in your reply so the delegating agent can handle it."}]},"role":"user","id":"{{message:21}}"},"surfaceOp":"append"}
{"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"session/title","data":{"title":"Reply with exactly WORKFLOW_CHILD_OK and","messageSeqs":[9],"source":{"kind":"fallback"}}}
{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"text","text":"WORKFLOW_CHILD_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:22}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1788699343148,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1788699343148,"index":0,"dt":[],"texts":["WORKFLOW_CHILD_OK"]},{"type":"chunk","time":1788699343148,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"WORKFLOW_CHILD_OK"}}},{"type":"chunk","time":1788699343148,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1788699343148,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"}
{"type":"step/end","data":{"turn":1,"step":1}}
{"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}}
@@ -0,0 +1,50 @@
{"type":"session","version":4,"id":"{{session:1}}","createdAt":1783950000000,"cwd":"{{cwd}}","isSeeded":false,"delegationDepth":0}
{"type":"permission/preset","data":{"preset":"danger-full-access"}}
{"type":"sandbox/mode","data":{"mode":"danger-full-access"}}
{"type":"approval/policy","data":{"policy":"never"}}
{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Run this advanced flow exactly once: list runtime inspect providers; discover providers through run_code when available; delegate once to a direct spawn child; run one workflow that delegates to another spawn child; list providers again; then reply with exactly ADVANCED_HEADLESS_OK."}],"source":{"kind":"user"},"role":"user","id":"{{message:1}}"}]}}
{"type":"turn/start","data":{"turn":1}}
{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
{"type":"step/start","data":{"turn":1,"step":1}}
{"type":"system/message","data":{"turn":1,"step":1,"message":{"role":"system","content":[{"type":"text","text":"{{system}}"}],"source":{"kind":"system-prompt"},"id":"{{message:2}}"}},"surfaceOp":"append"}
{"type":"user/message","data":{"content":[{"type":"text","text":"Run this advanced flow exactly once: list runtime inspect providers; discover providers through run_code when available; delegate once to a direct spawn child; run one workflow that delegates to another spawn child; list providers again; then reply with exactly ADVANCED_HEADLESS_OK."}],"source":{"kind":"user"},"role":"user","id":"{{message:1}}"},"surfaceOp":"append"}
{"type":"user/message","data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations.\n\nApproval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}],"source":{"kind":"runtime-context","form":"snapshot","sections":[{"name":"sandbox:policy","text":"Current DSH file policy: danger-full-access. The DSH file sandbox does not restrict file modifications by available operations."},{"name":"approval:policy","text":"Approval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`)."}]},"role":"user","id":"{{message:3}}"},"surfaceOp":"append"}
{"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}}
{"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
{"type":"session/title","data":{"title":"Run this advanced flow exactly","messageSeqs":[8],"source":{"kind":"fallback"}}}
{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-inspect-before","name":"cordis_inspect_list","arguments":"{}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1789544138268,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1789544138268,"index":0,"dt":[],"id":"advanced-inspect-before","name":"cordis_inspect_list","args":["{}"]},{"type":"chunk","time":1789544138268,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-inspect-before","name":"cordis_inspect_list","arguments":"{}"}}},{"type":"chunk","time":1789544138268,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1789544138268,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}
{"type":"tool/call","data":{"turn":1,"step":1,"callId":"advanced-inspect-before","name":"cordis_inspect_list","arguments":"{}"}}
{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"advanced-inspect-before"},"toolCallId":"advanced-inspect-before","content":[{"type":"text","text":"{\n \"providers\": [\n {\n \"platform\": \"host\",\n \"id\": \"Service\",\n \"description\": \"Progressive Host Service discovery: compact capability/signature directory, then one exact coding contract.\",\n \"methods\": [\n {\n \"name\": \"listService\",\n \"description\": \"Progressive Host Service discovery: compact capability/signature directory, then one exact coding contract.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"service\": {\n \"type\": \"string\",\n \"description\": \"Exact Service key. Omit it for the compact Service and method-signature directory.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"Compact Service directory, or one exact Service contract with only its referenced type declarations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Event\",\n \"description\": \"Progressive Host Event discovery: compact listener directory, then one exact event contract.\",\n \"methods\": [\n {\n \"name\": \"listEvents\",\n \"description\": \"Progressive Host Event discovery: compact listener directory, then one exact event contract.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"event\": {\n \"type\": \"string\",\n \"description\": \"Exact Event name. Omit it for the compact Event and listener-signature directory.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"Compact Event directory, or one exact Event contract with only its referenced type declarations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Config\",\n \"description\": \"Progressive live plugin Config discovery: paged entry directory with schema status, then one entry's exact JSON Schema.\",\n \"methods\": [\n {\n \"name\": \"listConfigs\",\n \"description\": \"Progressive live plugin Config discovery: paged entry directory with schema status, then one entry's exact JSON Schema.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"entry\": {\n \"type\": \"string\",\n \"description\": \"Exact Loader entry id from the directory; returns that entry's projected Config schema.\"\n },\n \"name\": {\n \"type\": \"string\",\n \"description\": \"Exact plugin package name; limits the directory to its entries.\"\n },\n \"offset\": {\n \"type\": \"number\",\n \"description\": \"Zero-based directory offset; defaults to 0.\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Directory page size, from 1 to 100; defaults to 25.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"One directory page of live entries with patch ids, Config status, total, and nextOffset, or one entry's projected JSON Schema with shared definitions, omission acceptance, and projection limitations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Tool\",\n \"description\": \"Tools visible to the requesting Agent, including scoped and dynamic registrations.\",\n \"methods\": [\n {\n \"name\": \"listTools\",\n \"description\": \"Return every Tool schema currently callable by this Agent.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {},\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"JSON data owned by this inspect provider.\"\n }\n }\n ]\n }\n ]\n}"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"}
{"type":"step/end","data":{"turn":1,"step":1}}
{"type":"step/start","data":{"turn":1,"step":2}}
{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-code","name":"run_code","arguments":"{\"code\":\"const inspected = await tools.cordis_inspect_list({}); return inspected.providers.map(provider => provider.id);\",\"description\":\"Discover runtime APIs through PTC\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1789544138288,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1789544138288,"index":0,"dt":[],"id":"advanced-code","name":"run_code","args":["{\"code\":\"const inspected = await tools.cordis_inspect_list({}); return inspected.providers.map(provider => provider.id);\",\"description\":\"Discover runtime APIs through PTC\"}"]},{"type":"chunk","time":1789544138288,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-code","name":"run_code","arguments":"{\"code\":\"const inspected = await tools.cordis_inspect_list({}); return inspected.providers.map(provider => provider.id);\",\"description\":\"Discover runtime APIs through PTC\"}"}}},{"type":"chunk","time":1789544138288,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1789544138288,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}
{"type":"tool/call","data":{"turn":1,"step":2,"callId":"advanced-code","name":"run_code","arguments":"{\"code\":\"const inspected = await tools.cordis_inspect_list({}); return inspected.providers.map(provider => provider.id);\",\"description\":\"Discover runtime APIs through PTC\"}"}}
{"type":"tool/ptc-dispatch-start","data":{"rootCallId":"advanced-code","parentCallId":"advanced-code","subCallId":"advanced-code:ptc:1","name":"cordis_inspect_list","arguments":{}}}
{"type":"tool/ptc-dispatch","data":{"rootCallId":"advanced-code","parentCallId":"advanced-code","subCallId":"advanced-code:ptc:1","name":"cordis_inspect_list","arguments":{},"isError":false,"content":[{"type":"text","text":"{\n \"providers\": [\n {\n \"platform\": \"host\",\n \"id\": \"Service\",\n \"description\": \"Progressive Host Service discovery: compact capability/signature directory, then one exact coding contract.\",\n \"methods\": [\n {\n \"name\": \"listService\",\n \"description\": \"Progressive Host Service discovery: compact capability/signature directory, then one exact coding contract.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"service\": {\n \"type\": \"string\",\n \"description\": \"Exact Service key. Omit it for the compact Service and method-signature directory.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"Compact Service directory, or one exact Service contract with only its referenced type declarations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Event\",\n \"description\": \"Progressive Host Event discovery: compact listener directory, then one exact event contract.\",\n \"methods\": [\n {\n \"name\": \"listEvents\",\n \"description\": \"Progressive Host Event discovery: compact listener directory, then one exact event contract.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"event\": {\n \"type\": \"string\",\n \"description\": \"Exact Event name. Omit it for the compact Event and listener-signature directory.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"Compact Event directory, or one exact Event contract with only its referenced type declarations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Config\",\n \"description\": \"Progressive live plugin Config discovery: paged entry directory with schema status, then one entry's exact JSON Schema.\",\n \"methods\": [\n {\n \"name\": \"listConfigs\",\n \"description\": \"Progressive live plugin Config discovery: paged entry directory with schema status, then one entry's exact JSON Schema.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"entry\": {\n \"type\": \"string\",\n \"description\": \"Exact Loader entry id from the directory; returns that entry's projected Config schema.\"\n },\n \"name\": {\n \"type\": \"string\",\n \"description\": \"Exact plugin package name; limits the directory to its entries.\"\n },\n \"offset\": {\n \"type\": \"number\",\n \"description\": \"Zero-based directory offset; defaults to 0.\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Directory page size, from 1 to 100; defaults to 25.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"One directory page of live entries with patch ids, Config status, total, and nextOffset, or one entry's projected JSON Schema with shared definitions, omission acceptance, and projection limitations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Tool\",\n \"description\": \"Tools visible to the requesting Agent, including scoped and dynamic registrations.\",\n \"methods\": [\n {\n \"name\": \"listTools\",\n \"description\": \"Return every Tool schema currently callable by this Agent.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {},\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"JSON data owned by this inspect provider.\"\n }\n }\n ]\n }\n ]\n}"}]}}
{"type":"tool/result","data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"advanced-code"},"toolCallId":"advanced-code","content":[{"type":"text","text":"[\n \"Service\",\n \"Event\",\n \"Config\",\n \"Tool\"\n]"}],"isError":false,"id":"{{message:7}}"}},"sourceEventSeqs":[19],"surfaceOp":"append"}
{"type":"step/end","data":{"turn":1,"step":2}}
{"type":"step/start","data":{"turn":1,"step":3}}
{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-direct-child","name":"subagent","arguments":"{\"description\":\"Check direct child\",\"prompt\":\"Reply with exactly DIRECT_CHILD_OK and nothing else.\",\"run_in_background\":false}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1789544138399,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1789544138399,"index":0,"dt":[],"id":"advanced-direct-child","name":"subagent","args":["{\"description\":\"Check direct child\",\"prompt\":\"Reply with exactly DIRECT_CHILD_OK and nothing else.\",\"run_in_background\":false}"]},{"type":"chunk","time":1789544138399,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-direct-child","name":"subagent","arguments":"{\"description\":\"Check direct child\",\"prompt\":\"Reply with exactly DIRECT_CHILD_OK and nothing else.\",\"run_in_background\":false}"}}},{"type":"chunk","time":1789544138399,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1789544138399,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}
{"type":"tool/call","data":{"turn":1,"step":3,"callId":"advanced-direct-child","name":"subagent","arguments":"{\"description\":\"Check direct child\",\"prompt\":\"Reply with exactly DIRECT_CHILD_OK and nothing else.\",\"run_in_background\":false}"}}
{"type":"subagent/catalog","data":{"version":0,"childId":"{{session:2}}","childCreatedAt":1789544138405,"mode":"one-shot","label":"Check direct child"}}
{"type":"tool/result","data":{"turn":1,"step":3,"message":{"role":"tool","source":{"kind":"tool","callId":"advanced-direct-child"},"toolCallId":"advanced-direct-child","content":[{"type":"text","text":"DIRECT_CHILD_OK"}],"isError":false,"id":"{{message:9}}"}},"sourceEventSeqs":[26],"surfaceOp":"append"}
{"type":"step/end","data":{"turn":1,"step":3}}
{"type":"step/start","data":{"turn":1,"step":4}}
{"type":"assistant/message","data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-workflow","name":"workflow","arguments":"{\"script\":\"phase('Delegate')\\nconst reply = await agent('Reply with exactly WORKFLOW_CHILD_OK and nothing else.', { label: 'workflow-child' })\\nreturn { reply }\",\"meta\":{\"name\":\"advanced-headless-snapshot\",\"description\":\"exercise one workflow child through the headless agent\"}}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:10}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1789544138465,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1789544138465,"index":0,"dt":[],"id":"advanced-workflow","name":"workflow","args":["{\"script\":\"phase('Delegate')\\nconst reply = await agent('Reply with exactly WORKFLOW_CHILD_OK and nothing else.', { label: 'workflow-child' })\\nreturn { reply }\",\"meta\":{\"name\":\"advanced-headless-snapshot\",\"description\":\"exercise one workflow child through the headless agent\"}}"]},{"type":"chunk","time":1789544138465,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-workflow","name":"workflow","arguments":"{\"script\":\"phase('Delegate')\\nconst reply = await agent('Reply with exactly WORKFLOW_CHILD_OK and nothing else.', { label: 'workflow-child' })\\nreturn { reply }\",\"meta\":{\"name\":\"advanced-headless-snapshot\",\"description\":\"exercise one workflow child through the headless agent\"}}"}}},{"type":"chunk","time":1789544138465,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1789544138465,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}
{"type":"tool/call","data":{"turn":1,"step":4,"callId":"advanced-workflow","name":"workflow","arguments":"{\"script\":\"phase('Delegate')\\nconst reply = await agent('Reply with exactly WORKFLOW_CHILD_OK and nothing else.', { label: 'workflow-child' })\\nreturn { reply }\",\"meta\":{\"name\":\"advanced-headless-snapshot\",\"description\":\"exercise one workflow child through the headless agent\"}}"}}
{"type":"tool-workflow/run-start","data":{"runId":"{{workflow:1}}","name":"advanced-headless-snapshot"}}
{"type":"subagent/catalog","data":{"version":0,"childId":"{{session:3}}","childCreatedAt":1789544138536,"mode":"one-shot"}}
{"type":"tool-workflow/agent-start","data":{"runId":"{{workflow:1}}","seq":1,"label":"workflow-child","phase":"Delegate","childId":"{{session:3}}"}}
{"type":"tool-workflow/agent-end","data":{"runId":"{{workflow:1}}","seq":1,"outcome":"completed"}}
{"type":"tool-workflow/run-end","data":{"runId":"{{workflow:1}}","stopReason":"completed"}}
{"type":"tool/result","data":{"turn":1,"step":4,"message":{"role":"tool","source":{"kind":"tool","callId":"advanced-workflow"},"toolCallId":"advanced-workflow","content":[{"type":"text","text":"workflow \"advanced-headless-snapshot\" completed (1 agent).\nReturn value:\n{\n \"reply\": \"WORKFLOW_CHILD_OK\"\n}"}],"isError":false,"id":"{{message:11}}"}},"sourceEventSeqs":[32],"surfaceOp":"append"}
{"type":"step/end","data":{"turn":1,"step":4}}
{"type":"step/start","data":{"turn":1,"step":5}}
{"type":"assistant/message","data":{"turn":1,"step":5,"message":{"role":"assistant","content":[{"type":"tool-call","id":"advanced-inspect-after","name":"cordis_inspect_list","arguments":"{}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:12}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1789544138602,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1789544138602,"index":0,"dt":[],"id":"advanced-inspect-after","name":"cordis_inspect_list","args":["{}"]},{"type":"chunk","time":1789544138602,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"advanced-inspect-after","name":"cordis_inspect_list","arguments":"{}"}}},{"type":"chunk","time":1789544138602,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1789544138602,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}
{"type":"tool/call","data":{"turn":1,"step":5,"callId":"advanced-inspect-after","name":"cordis_inspect_list","arguments":"{}"}}
{"type":"tool/result","data":{"turn":1,"step":5,"message":{"role":"tool","source":{"kind":"tool","callId":"advanced-inspect-after"},"toolCallId":"advanced-inspect-after","content":[{"type":"text","text":"{\n \"providers\": [\n {\n \"platform\": \"host\",\n \"id\": \"Service\",\n \"description\": \"Progressive Host Service discovery: compact capability/signature directory, then one exact coding contract.\",\n \"methods\": [\n {\n \"name\": \"listService\",\n \"description\": \"Progressive Host Service discovery: compact capability/signature directory, then one exact coding contract.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"service\": {\n \"type\": \"string\",\n \"description\": \"Exact Service key. Omit it for the compact Service and method-signature directory.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"Compact Service directory, or one exact Service contract with only its referenced type declarations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Event\",\n \"description\": \"Progressive Host Event discovery: compact listener directory, then one exact event contract.\",\n \"methods\": [\n {\n \"name\": \"listEvents\",\n \"description\": \"Progressive Host Event discovery: compact listener directory, then one exact event contract.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"event\": {\n \"type\": \"string\",\n \"description\": \"Exact Event name. Omit it for the compact Event and listener-signature directory.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"Compact Event directory, or one exact Event contract with only its referenced type declarations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Config\",\n \"description\": \"Progressive live plugin Config discovery: paged entry directory with schema status, then one entry's exact JSON Schema.\",\n \"methods\": [\n {\n \"name\": \"listConfigs\",\n \"description\": \"Progressive live plugin Config discovery: paged entry directory with schema status, then one entry's exact JSON Schema.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"entry\": {\n \"type\": \"string\",\n \"description\": \"Exact Loader entry id from the directory; returns that entry's projected Config schema.\"\n },\n \"name\": {\n \"type\": \"string\",\n \"description\": \"Exact plugin package name; limits the directory to its entries.\"\n },\n \"offset\": {\n \"type\": \"number\",\n \"description\": \"Zero-based directory offset; defaults to 0.\"\n },\n \"limit\": {\n \"type\": \"number\",\n \"description\": \"Directory page size, from 1 to 100; defaults to 25.\"\n }\n },\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"One directory page of live entries with patch ids, Config status, total, and nextOffset, or one entry's projected JSON Schema with shared definitions, omission acceptance, and projection limitations.\"\n }\n }\n ]\n },\n {\n \"platform\": \"host\",\n \"id\": \"Tool\",\n \"description\": \"Tools visible to the requesting Agent, including scoped and dynamic registrations.\",\n \"methods\": [\n {\n \"name\": \"listTools\",\n \"description\": \"Return every Tool schema currently callable by this Agent.\",\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {},\n \"additionalProperties\": false\n },\n \"outputSchema\": {\n \"description\": \"JSON data owned by this inspect provider.\"\n }\n }\n ]\n }\n ]\n}"}],"isError":false,"id":"{{message:13}}"}},"sourceEventSeqs":[42],"surfaceOp":"append"}
{"type":"step/end","data":{"turn":1,"step":5}}
{"type":"step/start","data":{"turn":1,"step":6}}
{"type":"assistant/message","data":{"turn":1,"step":6,"message":{"role":"assistant","content":[{"type":"text","text":"ADVANCED_HEADLESS_OK"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:14}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1789544138620,"chunk":{"type":"block-start","index":0,"blockType":"text"}},{"type":"text-chunks","time0":1789544138620,"index":0,"dt":[],"texts":["ADVANCED_HEADLESS_OK"]},{"type":"chunk","time":1789544138620,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"ADVANCED_HEADLESS_OK"}}},{"type":"chunk","time":1789544138620,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1789544138620,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"}
{"type":"step/end","data":{"turn":1,"step":6}}
{"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}}
@@ -67,7 +67,7 @@ interface ToolArgsMap {
} & Record<string, JsonValue>;
/** List every Cordis Inspect Provider currently known to the Host, including local Host Providers and the latest manifests synchronized from the Client. Each entry includes its platform, purpose, read-only methods, and input/output schemas. Call this Tool before writing or configuring a plugin, then select the provider and method for cordis_inspect_query from its result. Do not guess names or treat an Inspect method as a business Service that Plugin code can call. */
cordis_inspect_list: Record<string, JsonValue>;
/** Run a read-only query declared by an Inspect Provider. platform, provider, and method must come from cordis_inspect_list, and input must satisfy that method's schema. Use this Tool before writing plugin code to read exact Service methods, Event modes, Builtin signatures, Tool schemas, theme tokens, or live Slot trees and props. Host queries run locally. A Client query waits for the first valid page response and remains pending until a page answers or the Tool is cancelled. This Tool cannot invoke business Service methods or modify the runtime. */
/** Run a read-only query declared by an Inspect Provider. platform, provider, and method must come from cordis_inspect_list, and input must satisfy that method's schema. Use this Tool before writing plugin code to read exact Service methods, Event modes, plugin Config schemas, Tool schemas, theme tokens, or live Slot trees and props. Host queries run locally. A Client query waits for the first valid page response and remains pending until a page answers or the Tool is cancelled. This Tool cannot invoke business Service methods or modify the runtime. */
cordis_inspect_query: {
/** Runtime platform that owns the Provider. */
platform: "host" | "client";
@@ -55,7 +55,7 @@
},
{
"name": "cordis_inspect_query",
"description": "Run a read-only query declared by an Inspect Provider. platform, provider, and method must come from cordis_inspect_list, and input must satisfy that method's schema. Use this Tool before writing plugin code to read exact Service methods, Event modes, Builtin signatures, Tool schemas, theme tokens, or live Slot trees and props. Host queries run locally. A Client query waits for the first valid page response and remains pending until a page answers or the Tool is cancelled. This Tool cannot invoke business Service methods or modify the runtime.",
"description": "Run a read-only query declared by an Inspect Provider. platform, provider, and method must come from cordis_inspect_list, and input must satisfy that method's schema. Use this Tool before writing plugin code to read exact Service methods, Event modes, plugin Config schemas, Tool schemas, theme tokens, or live Slot trees and props. Host queries run locally. A Client query waits for the first valid page response and remains pending until a page answers or the Tool is cancelled. This Tool cannot invoke business Service methods or modify the runtime.",
"parameters": {
"type": "object",
"properties": {
@@ -15,7 +15,7 @@
{"type":"session/title","data":{"title":"Load the editing-cordis-compositions ski","messageSeqs":[8],"source":{"kind":"fallback"}}}
{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"Load the requested skill."},{"type":"tool-call","id":"call_skill_load","name":"skill","arguments":"{\"name\":\"editing-cordis-compositions\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:5}}"},"usage":{"inputTokens":100,"outputTokens":20,"cacheReadTokens":0,"reasoningTokens":5},"stream":[{"type":"chunk","time":1788699430688,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"reasoning-chunks","time0":1788699430688,"index":0,"dt":[],"texts":["Load the requested skill."]},{"type":"chunk","time":1788699430688,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1788699430688,"index":1,"dt":[],"id":"call_skill_load","name":"skill","args":["{\"name\":\"editing-cordis-compositions\"}"]},{"type":"chunk","time":1788699430688,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"Load the requested skill."}}},{"type":"chunk","time":1788699430688,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_skill_load","name":"skill","arguments":"{\"name\":\"editing-cordis-compositions\"}"}}},{"type":"chunk","time":1788699430688,"chunk":{"type":"usage","usage":{"inputTokens":100,"outputTokens":20,"cacheReadTokens":0,"reasoningTokens":5}}},{"type":"chunk","time":1788699430688,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}
{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_skill_load","name":"skill","arguments":"{\"name\":\"editing-cordis-compositions\"}"}}
{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_skill_load"},"toolCallId":"call_skill_load","content":[{"type":"text","text":"<skill_content name=\"editing-cordis-compositions\">\n<skill_resources>\nBase directory for this skill: {{cwd}}/.dsh/skills/editing-cordis-compositions\nResolve relative paths mentioned by this skill against the base directory before using them. Load referenced resources only as needed.\n</skill_resources>\n\n<skill_instructions>\n# Editing Cordis compositions\n\nAgent presets are ordinary `@deepseek-ai/dsh-agent-preset` declarations carried by bundle patches. Nothing edits a declaration in place: a preset is created or changed by installing a bundle whose patch declares or overrides it. This file is the complete contract; do not read DSH package sources to infer formats.\n\n## Where declarations live\n\nThe shipped Web presets are `presets/<id>.patch.yml` files of the `@deepseek-ai/dsh-web-app` bundle, ids `standard`, `ptc`, `minimal` and `cordis`. Installed, that bundle sits at `$DSH_HOME/profiles/<profile>/node_modules/@deepseek-ai/dsh-web-app/`; in a source checkout of DSH it is `packages/bundle/web-app/`. Read one file with `cat` when you need a template; `minimal.patch.yml` is the shortest.\n\nA declaration row has these `config` fields: `id` (required, lowercase letters, digits and hyphens), `plugins` (required Cordis entry list), and optional `name`, `description` and `order` (roster position). The Loader row `id` is `preset-<id>` by convention.\n\n## Create a preset\n\nWrite a bundle directory in the workspace with exactly two files, then install it.\n\n`review-preset/package.json`:\n\n```json\n{\n \"name\": \"@local/dsh-review-preset\",\n \"version\": \"1.0.0\",\n \"private\": true,\n \"type\": \"module\",\n \"dsh\": { \"bundle\": { \"patch\": \"./cordis.patch.yml\" } }\n}\n```\n\n`review-preset/cordis.patch.yml`:\n\n```yaml\n- insert:\n - id: preset-review\n name: '@deepseek-ai/dsh-agent-preset'\n config:\n id: review\n name: Review\n description: Reviews changes with the shell only.\n order: 10\n plugins:\n - id: persona\n name: '@deepseek-ai/dsh-persona'\n config:\n prefix: You review software changes.\n - id: tool-bash\n name: '@deepseek-ai/dsh-tool-bash'\n```\n\nInstall with `plugin_manager`, `action: install_bundle`, `target` set to the absolute bundle directory. It runs package installation and bundle selection itself; do not reproduce those steps with shell commands.\n\n## Change a shipped preset\n\nOverride the declaration by its Loader row id instead of inserting. The override replaces the complete `config`, so restate `id`, `plugins` and every other field the shipped file carries:\n\n```yaml\n- id: preset-standard\n name: '@deepseek-ai/dsh-agent-preset'\n config:\n id: standard\n order: 1\n plugins:\n # the shipped list with your changes\n```\n\n## Verify\n\n`plugin_manager` `list_bundles` lists the installed bundle; `list_plugins` shows the `preset-<id>` row with its activation state. A declaration whose activation fails stays on the roster with its diagnostic and cannot compose a session until the bundle is fixed and reinstalled. Existing sessions and their children keep the plugin revision they started with; validate changed behavior in a new session. Installing a bundle executes plugin code in the Host process, so it requires Full access or approval.\n\n## Choose plugin placement\n\nHost plugins supply shared services: tools and prompt registries, the Agent loop, sessions, persistence, settings, sandbox policy, model routes, and subagent backends. Preset plugins contribute scoped tools, persona, prompt sections, and policies to those registries. Preset revisions are eagerly activated once and shared by their selecting Agents.\n\nA preset plugin that supplies a service must isolate the provider and all its consumers in the same realm. A service consumed by Host plugins belongs in the Host configuration. Scope controls contributions and event visibility; `isolate` controls service instances. Use ordinary Cordis groups for nested plugin lists. Keep `!!js` expressions only in plugin configuration or `disabled`; the Loader evaluates them when activating the child plugin. Resolve assets from installed packages rather than a preset directory.\n\n## Extend the Host\n\nFor new plugin code or profile-wide capabilities, load `cordis-plugin-development`, author a workspace bundle, and install it with `plugin_manager`. Inspect available APIs through `cordis_inspect_list` and `cordis_inspect_query`; those tools do not invoke Remote methods. Verify the installed capability before reporting completion.\n</skill_instructions>\n</skill_content>"}],"isError":false,"id":"{{message:6}}"}},"sourceEventSeqs":[15],"surfaceOp":"append"}
{"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"call_skill_load"},"toolCallId":"call_skill_load","content":[{"type":"text","text":"<skill_content name=\"editing-cordis-compositions\">\n<skill_resources>\nBase directory for this skill: {{cwd}}/.dsh/skills/editing-cordis-compositions\nResolve relative paths mentioned by this skill against the base directory before using them. Load referenced resources only as needed.\n</skill_resources>\n\n<skill_instructions>\n# Editing Cordis compositions\n\nAgent presets are ordinary `@deepseek-ai/dsh-agent-preset` declarations carried by bundle patches. Nothing edits a declaration in place: a preset is created or changed by installing a bundle whose patch declares or overrides it. This file states the declaration format; when it leaves a question open, read the `@deepseek-ai/dsh-agent-preset` README and `lib/types` declarations under the `packageDir` that `cordis_inspect_query` `Config.listConfigs` returns when queried with a `preset-<id>` row's `entry` id, or `packages/preset/agent-preset/src` in a source checkout.\n\n## Where declarations live\n\nThe shipped Web presets are `presets/<id>.patch.yml` files of the `@deepseek-ai/dsh-web-app` bundle, ids `standard`, `ptc`, `minimal` and `cordis`. Installed, the bundle resolves from the dsh installation, not the profile; querying `Config.listConfigs` with the `entry` id of any `preset-<id>` row it declares returns that `packageDir`. In a source checkout of DSH it is `packages/bundle/web-app/`. Read one file with the file-read tool when you need a template; `minimal.patch.yml` is the shortest. In Desktop the bundle sits inside `app.asar`, which shell commands cannot open. Load `cordis-composition-reference` for the patch dialect and the list of plugin packages a preset can mount.\n\nA declaration row has these `config` fields: `id` (required, lowercase letters, digits and hyphens), `plugins` (required Cordis entry list), and optional `name`, `description` and `order` (roster position). The Loader row `id` is `preset-<id>` by convention.\n\n## Create a preset\n\nWrite a bundle directory in the workspace with exactly two files, then install it.\n\n`review-preset/package.json`:\n\n```json\n{\n \"name\": \"@local/dsh-review-preset\",\n \"version\": \"1.0.0\",\n \"private\": true,\n \"type\": \"module\",\n \"dsh\": { \"bundle\": { \"patch\": \"./cordis.patch.yml\" } }\n}\n```\n\n`review-preset/cordis.patch.yml`:\n\n```yaml\n- insert:\n - id: preset-review\n name: '@deepseek-ai/dsh-agent-preset'\n config:\n id: review\n name: Review\n description: Reviews changes with the shell only.\n order: 10\n plugins:\n - id: persona\n name: '@deepseek-ai/dsh-persona'\n config:\n prefix: You review software changes.\n - id: tool-bash\n name: '@deepseek-ai/dsh-tool-bash'\n```\n\nInstall with `plugin_manager`, `action: install_bundle`, `target` set to the absolute bundle directory. It runs package installation and bundle selection itself; do not reproduce those steps with shell commands.\n\n## Change a shipped preset\n\nOverride the declaration by its Loader row id instead of inserting. The override replaces the complete `config`, so restate `id`, `plugins` and every other field the shipped file carries:\n\n```yaml\n- id: preset-standard\n name: '@deepseek-ai/dsh-agent-preset'\n config:\n id: standard\n order: 1\n plugins:\n # the shipped list with your changes\n```\n\n## Migrate a legacy preset\n\nBefore declaration rows, a user preset was a directory `$DSH_HOME/.agent-presets/<id>/` holding `preset.yml` (display `name`, `description`, `order`) and `agent.cordis.yml` (the plugin entry list). Nothing reads that directory any more. To migrate one, create a bundle as above whose declaration takes `id` from the directory name, `name`, `description`, and `order` from `preset.yml`, and `plugins` from `agent.cordis.yml` verbatim; check each plugin name against `cordis-composition-reference` because packages renamed since the preset was written fail at activation. Install it, verify the row, then delete the legacy directory.\n\n## Verify\n\n`plugin_manager` `list_bundles` lists the installed bundle; `list_plugins` shows the `preset-<id>` row with its activation state. A declaration whose activation fails stays on the roster with its diagnostic and cannot compose a session until the bundle is fixed and reinstalled. Existing sessions and their children keep the plugin revision they started with; validate changed behavior in a new session. Installing a bundle executes plugin code in the Host process, so it requires Full access or approval.\n\n## Choose plugin placement\n\nHost plugins supply shared services: tools and prompt registries, the Agent loop, sessions, persistence, settings, sandbox policy, model routes, and subagent backends. Preset plugins contribute scopLine truncated
{"type":"step/end","data":{"turn":1,"step":1}}
{"type":"step/start","data":{"turn":1,"step":2}}
{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The skill is loaded."},{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:7}}"},"usage":{"inputTokens":180,"outputTokens":10,"cacheReadTokens":0,"reasoningTokens":4},"stream":[{"type":"chunk","time":1788699430709,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}},{"type":"reasoning-chunks","time0":1788699430709,"index":0,"dt":[],"texts":["The skill is loaded."]},{"type":"chunk","time":1788699430709,"chunk":{"type":"block-start","index":1,"blockType":"text"}},{"type":"text-chunks","time0":1788699430709,"index":1,"dt":[],"texts":["DONE"]},{"type":"chunk","time":1788699430709,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The skill is loaded."}}},{"type":"chunk","time":1788699430709,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"DONE"}}},{"type":"chunk","time":1788699430709,"chunk":{"type":"usage","usage":{"inputTokens":180,"outputTokens":10,"cacheReadTokens":0,"reasoningTokens":4}}},{"type":"chunk","time":1788699430709,"chunk":{"type":"finish","reason":{"kind":"stop"}}}]},"surfaceOp":"append"}
+1 -1
View File
@@ -12,7 +12,7 @@
- button "Context injection skill-catalog"
- button "Think"
- button "Skill editing-cordis-compositions" [expanded]
- region "Instructions": "Instructions <skill_content name=\"editing-cordis-compositions\"> <skill_resources> Base directory for this skill: {{cwd}}/.dsh/skills/editing-cordis-compositions Resolve relative paths mentioned by this skill against the base directory before using them. Load referenced resources only as needed. </skill_resources> <skill_instructions> # Editing Cordis compositions Agent presets are ordinary `@deepseek-ai/dsh-agent-preset` declarations carried by bundle patches. Nothing edits a declaration in place: a preset is created or changed by installing a bundle whose patch declares or overrides it. This file is the complete contract; do not read DSH package sources to infer formats. ## Where declarations live The shipped Web presets are `presets/<id>.patch.yml` files of the `@deepseek-ai/dsh-web-app` bundle, ids `standard`, `ptc`, `minimal` and `cordis`. Installed, that bundle sits at `$DSH_HOME/profiles/<profile>/node_modules/@deepseek-ai/dsh-web-app/`; in a source checkout of DSH it is `packages/bundle/web-app/`. Read one file with `cat` when you need a template; `minimal.patch.yml` is the shortest. A declaration row has these `config` fields: `id` (required, lowercase letters, digits and hyphens), `plugins` (required Cordis entry list), and optional `name`, `description` and `order` (roster position). The Loader row `id` is `preset-<id>` by convention. ## Create a preset Write a bundle directory in the workspace with exactly two files, then install it. `review-preset/package.json`: ```json { \"name\": \"@local/dsh-review-preset\", \"version\": \"1.0.0\", \"private\": true, \"type\": \"module\", \"dsh\": { \"bundle\": { \"patch\": \"./cordis.patch.yml\" } } } ``` `review-preset/cordis.patch.yml`: ```yaml - insert: - id: preset-review name: '@deepseek-ai/dsh-agent-preset' config: id: review name: Review description: Reviews changes with the shell only. order: 10 plugins: - id: persona name: '@deepseek-ai/dsh-persona' config: prefix: You review software changes. - id: tool-bash name: '@deepseek-ai/dsh-tool-bash' ``` Install with `plugin_manager`, `action: install_bundle`, `target` set to the absolute bundle directory. It runs package installation and bundle selection itself; do not reproduce those steps with shell commands. ## Change a shipped preset Override the declaration by its Loader row id instead of inserting. The override replaces the complete `config`, so restate `id`, `plugins` and every other field the shipped file carries: ```yaml - id: preset-standard name: '@deepseek-ai/dsh-agent-preset' config: id: standard order: 1 plugins: # the shipped list with your changes ``` ## Verify `plugin_manager` `list_bundles` lists the installed bundle; `list_plugins` shows the `preset-<id>` row with its activation state. A declaration whose activation fails stays on the roster with its diagnostic and cannot compose a session until the bundle is fixed and reinstalled. Existing sessions and their children keep the plugin revision they started with; validate changed behavior in a new session. Installing a bundle executes plugin code in the Host process, so it requires Full access or approval. ## Choose plugin placement Host plugins supply shared services: tools and prompt registries, the Agent loop, sessions, persistence, settings, sandbox policy, model routes, and subagent backends. Preset plugins contribute scoped tools, persona, prompt sections, and policies to those registries. Preset revisions are eagerly activated once and shared by their selecting Agents. A preset plugin that supplies a service must isolate the provider and all its consumers in the same realm. A service consumed by Host plugins belongs in the Host configuration. Scope controls contributions and event visibility; `isolate` controls service instances. Use ordinary Cordis groups for nested plugin lists. Keep `!!js` expressions only in plugin configuration or `disabled`; the Loader evaluates them when activating the child plugin. Resolve assets from installed packages rather than a preset directory. ## Extend the Host For new plugin code or profile-wide capabilities, load `cordis-plugin-development`, author a workspace bundle, and install it with `plugin_manager`. Inspect available APIs through `cordis_inspect_list` and `cordis_inspect_query`; those tools do not invoke Remote methods. Verify the installed capability before reporting completion. </skill_instructions> </skill_content>"
- region "Instructions": "Instructions <skill_content name=\"editing-cordis-compositions\"> <skill_resources> Base directory for this skill: {{cwd}}/.dsh/skills/editing-cordis-compositions Resolve relative paths mentioned by this skill against the base directory before using them. Load referenced resources only as needed. </skill_resources> <skill_instructions> # Editing Cordis compositions Agent presets are ordinary `@deepseek-ai/dsh-agent-preset` declarations carried by bundle patches. Nothing edits a declaration in place: a preset is created or changed by installing a bundle whose patch declares or overrides it. This file states the declaration format; when it leaves a question open, read the `@deepseek-ai/dsh-agent-preset` README and `lib/types` declarations under the `packageDir` that `cordis_inspect_query` `Config.listConfigs` returns when queried with a `preset-<id>` row's `entry` id, or `packages/preset/agent-preset/src` in a source checkout. ## Where declarations live The shipped Web presets are `presets/<id>.patch.yml` files of the `@deepseek-ai/dsh-web-app` bundle, ids `standard`, `ptc`, `minimal` and `cordis`. Installed, the bundle resolves from the dsh installation, not the profile; querying `Config.listConfigs` with the `entry` id of any `preset-<id>` row it declares returns that `packageDir`. In a source checkout of DSH it is `packages/bundle/web-app/`. Read one file with the file-read tool when you need a template; `minimal.patch.yml` is the shortest. In Desktop the bundle sits inside `app.asar`, which shell commands cannot open. Load `cordis-composition-reference` for the patch dialect and the list of plugin packages a preset can mount. A declaration row has these `config` fields: `id` (required, lowercase letters, digits and hyphens), `plugins` (required Cordis entry list), and optional `name`, `description` and `order` (roster position). The Loader row `id` is `preset-<id>` by convention. ## Create a preset Write a bundle directory in the workspace with exactly two files, then install it. `review-preset/package.json`: ```json { \"name\": \"@local/dsh-review-preset\", \"version\": \"1.0.0\", \"private\": true, \"type\": \"module\", \"dsh\": { \"bundle\": { \"patch\": \"./cordis.patch.yml\" } } } ``` `review-preset/cordis.patch.yml`: ```yaml - insert: - id: preset-review name: '@deepseek-ai/dsh-agent-preset' config: id: review name: Review description: Reviews changes with the shell only. order: 10 plugins: - id: persona name: '@deepseek-ai/dsh-persona' config: prefix: You review software changes. - id: tool-bash name: '@deepseek-ai/dsh-tool-bash' ``` Install with `plugin_manager`, `action: install_bundle`, `target` set to the absolute bundle directory. It runs package installation and bundle selection itself; do not reproduce those steps with shell commands. ## Change a shipped preset Override the declaration by its Loader row id instead of inserting. The override replaces the complete `config`, so restate `id`, `plugins` and every other field the shipped file carries: ```yaml - id: preset-standard name: '@deepseek-ai/dsh-agent-preset' config: id: standard order: 1 plugins: # the shipped list with your changes ``` ## Migrate a legacy preset Before declaration rows, a user preset was a directory `$DSH_HOME/.agent-presets/<id>/` holding `preset.yml` (display `name`, `description`, `order`) and `agent.cordis.yml` (the plugin entry list). Nothing reads that directory any more. To migrate one, create a bundle as above whose declaration takes `id` from the directory name, `name`, `description`, and `order` from `preset.yml`, and `plugins` from `agent.cordis.yml` verbatim; check each plugin name against `cordis-composition-reference` because packages renamed since the preset was written fail at activation. Install it, verify the row, then delete the legacy directory. ## Verify `plugin_manager` `list_bundles` lists the installed bundle; `list_plugins` shows the `preset-<id>` row with its activation state. A declaration whose activation fails stays on the roster with its diagnostic and cannot compose a session until the bundle is fixed and reinstalled. Existing sessions and their children keep the plugin revision they started with; validate changed behavior in a new session. Installing a bundle executes plugin code in the Host process, so it requires Full access or approval. ## Choose plugin placement Host plugins supply shared services: tools and prompt registries, the Agent loop, sessions, persistence, settings, sandbox policy, model routes, and subagent backends. Preset plugins contribute scoped tools, persona, prompt sections, and policies to those registries. Preset revisions are eagerly activated once and shared by their selecting Agents. A preset plugin that supplies a service must isolate the provider and all its consumers in the same realm. A service consumed by Host plugins belongs in the Host configuration. Scope controls contributions and event visibility; `isolate` controls service inLine truncated
- button "Inspect"
- button "Think"
- paragraph: DONE