mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-14 04:01:35 +00:00
fix(web): open delivered files using the viewed session scope
This commit is contained in:
@@ -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 .agents/notes/implemented/feature/2026-09-09-present-filesystem-access.md
|
||||
2026-09-09-present-filesystem-access.md: 24b0e767d96261a95e449278d0919c381141f50d
|
||||
2026-09-09-present-filesystem-access.zh.md: 2f4dc550823bd6c8de6ad61c113d58e3f238bbb5
|
||||
2026-09-09-present-filesystem-access.md: 5c9d5bec47c73ac1c5952b887d56e582148a4cea
|
||||
2026-09-09-present-filesystem-access.zh.md: 6387f07b17276b3455d19d650b4ae031a7efda4c
|
||||
|
||||
@@ -12,7 +12,7 @@ Generated files commonly live outside the workspace, especially in `/tmp`. Works
|
||||
|
||||
`present` accepts existing regular files accessible through its composed `ctx.fs`, with relative paths resolved against the Session working directory. There is no workspace containment check or special temporary-directory allowlist. Missing files, directories, final symbolic links, and provider failures reject the declaration. A sandbox's private temporary files remain unavailable when the filesystem provider cannot see them.
|
||||
|
||||
Native actions resolve or resume the viewed Session's Agent and use its `workspaceFiles.stat`. The resulting canonical process path must map from a Host path back to the same process path through that Agent's filesystem. Absent or different mappings produce 422 and a localized Sidebar-preview suggestion. This conservatively supports Host paths that share their canonical process spelling; providers with only a nonidentity Host mapping can still serve previews. A same-named local file never substitutes for an unmapped provider file.
|
||||
Native actions use the viewed Session header returned with the declaration to form the `workspaceFiles.stat` scope: its cwd, or the deployment workspace root when absent. The same composed filesystem serves Sidebar previews and native validation without activating an Agent, including for child Sessions. The resulting canonical process path must map from a Host path back to the same process path through that filesystem. Absent or different mappings produce 422 and a localized Sidebar-preview suggestion. This conservatively supports Host paths that share their canonical process spelling; providers with only a nonidentity Host mapping can still serve previews. A same-named local file never substitutes for an unmapped provider file.
|
||||
|
||||
This replaces the workspace-only access rule in the [source-file delivery decision](2026-09-08-present-workspace-source-files.md). That note continues to own content-free declarations, Session events, and editing current sources. The request still selects only saved Session/event/file coordinates, never an arbitrary browser-supplied path.
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Status: implemented
|
||||
|
||||
`present` 接受所组合的 `ctx.fs` 可访问的已有普通文件,相对路径按 Session 工作目录解析。不检查工作区包含关系,也不另设临时目录白名单。文件缺失、为目录、最终路径为符号链接或提供方出错时,声明失败。文件系统提供方不可见的沙箱私有临时文件仍不可用。
|
||||
|
||||
原生操作查找或恢复当前查看的 Session 的 Agent,并调用其 `workspaceFiles.stat`。得到的规范化进程路径必须能通过该 Agent 的文件系统从 Host 路径映射回同一进程路径。映射缺失或不同会返回 422,并显示使用侧栏预览的本地化提示。这只支持规范化后与进程路径写法相同的 Host 路径;仅支持不同写法的 Host 映射的提供方仍可提供预览。没有映射时,本机同名文件不能替代提供方文件。
|
||||
原生操作使用与声明一起返回的当前 Session header,为 `workspaceFiles.stat` 提供工作目录:优先使用其中的 cwd,未记录时使用部署的工作目录。侧栏预览和原生校验使用同一组合文件系统,无需启动 Agent,子会话也适用。得到的规范化进程路径必须能通过该文件系统从 Host 路径映射回同一进程路径。映射缺失或不同会返回 422,并显示使用侧栏预览的本地化提示。这只支持规范化后与进程路径写法相同的 Host 路径;仅支持不同写法的 Host 映射的提供方仍可提供预览。没有映射时,本机同名文件不能替代提供方文件。
|
||||
|
||||
本决策替代[源文件交付决策](2026-09-08-present-workspace-source-files.zh.md)中只允许工作区文件的访问规则。原说明继续负责不保存内容的声明、Session 事件和编辑当前源文件。请求仍只按已保存的 Session、事件和文件索引选择文件,不接受浏览器提供的任意路径。
|
||||
|
||||
|
||||
@@ -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: 1b7a52346a95a4933362f646f5873c49fc1bd71b
|
||||
config-catalog.zh.md: 8100201248be35987ac536a5dcf9d2b9c90f4a17
|
||||
config-catalog.md: 9adf44b995601196b602e9219e53c52208039051
|
||||
config-catalog.zh.md: e2f6c6babc18bfa689f6830c81231012b4d9de18
|
||||
|
||||
@@ -3459,7 +3459,7 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co
|
||||
- `@deepseek-ai/dsh-client-ui-commands` ([`packages/client/ui-commands/src/index.ts`](../packages/client/ui-commands/src/index.ts))
|
||||
- `@deepseek-ai/dsh-client-ui-conversation` ([`packages/client/ui-conversation/src/index.ts`](../packages/client/ui-conversation/src/index.ts))
|
||||
- `@deepseek-ai/dsh-client-ui-cordis` ([`packages/extensions/ui-cordis/src/index.ts`](../packages/extensions/ui-cordis/src/index.ts))
|
||||
- `@deepseek-ai/dsh-client-ui-deliverables` — requires `systemPrompt` · `connection` · `sessionQuery` · `sessionController` ([`packages/client/ui-deliverables/src/index.ts`](../packages/client/ui-deliverables/src/index.ts))
|
||||
- `@deepseek-ai/dsh-client-ui-deliverables` — requires `systemPrompt` · `connection` · `sessionQuery` · `sessionController` · `workspaceFiles` · `fs` · `sandboxPolicy` ([`packages/client/ui-deliverables/src/index.ts`](../packages/client/ui-deliverables/src/index.ts))
|
||||
- `@deepseek-ai/dsh-client-ui-directory-picker-browse` ([`packages/client/ui-directory-picker-browse/src/index.ts`](../packages/client/ui-directory-picker-browse/src/index.ts))
|
||||
- `@deepseek-ai/dsh-client-ui-directory-picker-native` ([`packages/client/ui-directory-picker-native/src/index.ts`](../packages/client/ui-directory-picker-native/src/index.ts))
|
||||
- `@deepseek-ai/dsh-client-ui-goal` ([`packages/client/ui-goal/src/index.ts`](../packages/client/ui-goal/src/index.ts))
|
||||
|
||||
@@ -3461,7 +3461,7 @@ export interface Config {
|
||||
- `@deepseek-ai/dsh-client-ui-commands`([`packages/client/ui-commands/src/index.ts`](../packages/client/ui-commands/src/index.ts))
|
||||
- `@deepseek-ai/dsh-client-ui-conversation`([`packages/client/ui-conversation/src/index.ts`](../packages/client/ui-conversation/src/index.ts))
|
||||
- `@deepseek-ai/dsh-client-ui-cordis`([`packages/extensions/ui-cordis/src/index.ts`](../packages/extensions/ui-cordis/src/index.ts))
|
||||
- `@deepseek-ai/dsh-client-ui-deliverables` — 需要 `systemPrompt` · `connection` · `sessionQuery` · `sessionController`([`packages/client/ui-deliverables/src/index.ts`](../packages/client/ui-deliverables/src/index.ts))
|
||||
- `@deepseek-ai/dsh-client-ui-deliverables` — 需要 `systemPrompt` · `connection` · `sessionQuery` · `sessionController` · `workspaceFiles` · `fs` · `sandboxPolicy`([`packages/client/ui-deliverables/src/index.ts`](../packages/client/ui-deliverables/src/index.ts))
|
||||
- `@deepseek-ai/dsh-client-ui-directory-picker-browse`([`packages/client/ui-directory-picker-browse/src/index.ts`](../packages/client/ui-directory-picker-browse/src/index.ts))
|
||||
- `@deepseek-ai/dsh-client-ui-directory-picker-native`([`packages/client/ui-directory-picker-native/src/index.ts`](../packages/client/ui-directory-picker-native/src/index.ts))
|
||||
- `@deepseek-ai/dsh-client-ui-goal`([`packages/client/ui-goal/src/index.ts`](../packages/client/ui-goal/src/index.ts))
|
||||
|
||||
@@ -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-deliverables/README.md
|
||||
README.md: 5ce7eb51df13e23a01a9ca05291c30a93e6d6d0c
|
||||
README.zh.md: 9d25bbe4f76b3bf14807ea741f4176ececbe94b7
|
||||
README.md: 857b71c5e0f3e4ba0dac0e8453492684c76ca0e4
|
||||
README.zh.md: 8e801a164c8a60de4aea8ff6a224e4823e2d3ccc
|
||||
|
||||
@@ -52,7 +52,7 @@ The closing prose carries the same vocabulary: an inline-code token resolves by
|
||||
|
||||
The Node half registers the static `ui:deliverable-file-references` system-prompt section asking the model to mention primary files from successful creation or modification calls and to write those and any other changed-file references as Markdown inline code. The browser half registers a wrapper around `ProducedFiles` and explicit deliveries into the chat view's `conversation.chat.turnTail` hole. `deliverablesDefinition` folds each Turn's successful first-party mutation calls into `DeliverablesTurnData` from the validated raw arguments of `write`, `edit`, and mutating `str_replace_editor` commands. Reads, deletes, unsupported tools, malformed calls, and failed results contribute nothing. A new mutation tool needs an explicit Client contribution before it joins the list. The package also provides the `chatFileMentions` service the chat view consults per closing message; composing the plugin out removes both surfaces and leaves the view's empty chain at zero cost.
|
||||
|
||||
Native opening uses an authenticated POST addressed by the viewed Session, event sequence, and original file index. The Host resolves or resumes that Session’s Agent and checks the declaration through its `workspaceFiles.stat`, using the same filesystem as Sidebar previews. Native actions require the canonical process path to map from a Host path back to that same process path. Providers without this mapping return 422 and the card directs the user to Sidebar preview; a same-named Host file is insufficient. The same configured desktop availability governs metadata and execution. Edits affect subsequent opens; deletion returns an error. No file-content copy or attachment is created. Plugin disposal cancels and awaits pending native-open requests.
|
||||
Native opening uses an authenticated POST addressed by the viewed Session, event sequence, and original file index. The Host reads the viewed Session header with the declaration and passes its cwd, or the deployment workspace root when absent, to `workspaceFiles.stat`. This uses the same composed filesystem as Sidebar previews and does not activate an Agent, including for child Sessions. Native actions require the canonical process path to map from a Host path back to that same process path. Providers without this mapping return 422 and the card directs the user to Sidebar preview; a same-named Host file is insufficient. The same configured desktop availability governs metadata and execution. Edits affect subsequent opens; deletion returns an error. No file-content copy or attachment is created. Plugin disposal cancels and awaits pending native-open requests.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ Web 的 `standard`、`ptc` 与 `cordis` preset 提供 `present` 用于声明交
|
||||
|
||||
Node 半部注册静态 `ui:deliverable-file-references` 系统提示词段,要求模型点名成功创建或修改的主要文件,并把这些文件以及正文中提到的其他本轮变更文件写成 Markdown 行内代码。浏览器半部把组合 `ProducedFiles` 与显式交付的包装组件注册进 chat 视图的 `conversation.chat.turnTail` 洞。`deliverablesDefinition` 根据 `write`、`edit` 和有修改作用的 `str_replace_editor` 命令中经过校验的原始参数,把每个轮次成功的第一方修改调用折叠进 `DeliverablesTurnData`。读取、删除、不受支持的工具、格式错误的调用和失败结果不贡献任何条目。新的修改工具必须增加显式 Client contribution 才能加入列表。本包还提供 chat 视图按收尾消息查询的 `chatFileMentions` 服务;把插件组合出去会同时移除两个表面,视图的空链以零成本留下。
|
||||
|
||||
原生打开使用经过认证的 POST,通过当前查看的 Session、事件序号和原始文件索引定位声明。Host 查找或恢复该 Session 的 Agent,通过其 `workspaceFiles.stat` 检查声明,与侧栏预览使用同一文件系统。原生操作要求规范化的进程路径能从 Host 路径映射回同一进程路径。提供方没有这种映射时返回 422,卡片提示使用侧栏预览;Host 上存在同名文件并不足够。同一份桌面可用性配置同时约束信息查询和实际执行。编辑会影响后续打开的内容;删除后返回错误。不创建文件内容副本或附件。插件释放时取消并等待进行中的原生打开请求。
|
||||
原生打开使用经过认证的 POST,通过当前查看的 Session、事件序号和原始文件索引定位声明。Host 读取声明及当前查看的 Session header,将其中的 cwd 传给 `workspaceFiles.stat`;未记录 cwd 时使用部署的工作目录。它与侧栏预览使用同一组合文件系统,无需启动 Agent,子会话也适用。原生操作要求规范化的进程路径能从 Host 路径映射回同一进程路径。提供方没有这种映射时返回 422,卡片提示使用侧栏预览;Host 上存在同名文件并不足够。同一份桌面可用性配置同时约束信息查询和实际执行。编辑会影响后续打开的内容;删除后返回错误。不创建文件内容副本或附件。插件释放时取消并等待进行中的原生打开请求。
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
@@ -72,7 +72,8 @@
|
||||
"@deepseek-ai/dsh-api-workspace-files": "workspace:^",
|
||||
"@deepseek-ai/dsh-fs": "workspace:^",
|
||||
"@deepseek-ai/dsh-fs-local": "workspace:^",
|
||||
"@deepseek-ai/dsh-agent": "workspace:^"
|
||||
"@deepseek-ai/dsh-agent": "workspace:^",
|
||||
"@deepseek-ai/dsh-sandbox-policy": "workspace:^"
|
||||
},
|
||||
"files": [
|
||||
"lib/index.js",
|
||||
|
||||
@@ -11,7 +11,7 @@ import type {} from '@deepseek-ai/dsh-system-prompt'
|
||||
import { registerPresentOpen } from './present-open.ts'
|
||||
|
||||
/** Services required for file-reference guidance and authenticated native opens of declared files. */
|
||||
export const inject = ['systemPrompt', 'connection', 'sessionQuery', 'sessionController']
|
||||
export const inject = ['systemPrompt', 'connection', 'sessionQuery', 'sessionController', 'workspaceFiles', 'fs', 'sandboxPolicy']
|
||||
|
||||
/** Stable final-response guidance owned by the matching renderer. */
|
||||
const FILE_REFERENCE_PROMPT = 'When you successfully create or modify files, mention the primary outputs in your final response. '
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { Context } from '@deepseek-ai/cordis'
|
||||
import type {} from '@deepseek-ai/dsh-api-session-controller'
|
||||
import type {} from '@deepseek-ai/dsh-api-workspace-files'
|
||||
import type {} from '@deepseek-ai/dsh-fs'
|
||||
import type {} from '@deepseek-ai/dsh-sandbox-policy'
|
||||
import { remoteErrorOf } from '@deepseek-ai/dsh-typert-protocol'
|
||||
import type {} from '@deepseek-ai/dsh-client-connection'
|
||||
import type {} from '@deepseek-ai/dsh-session-query'
|
||||
@@ -54,19 +55,17 @@ async function handlePresentOpen(ctx: Context, request: Request): Promise<Respon
|
||||
try {
|
||||
request.signal.throwIfAborted()
|
||||
if (!ctx.sessionController.workspaceDesktop().available) return new Response('Host desktop unavailable.', { status: 409 })
|
||||
const { target } = await ctx.sessionQuery.readEvent({
|
||||
const { target, session } = await ctx.sessionQuery.readEvent({
|
||||
sessionId: id as SessionId, seq: Number(seq) as SessionSeq, before: 0, after: 0,
|
||||
}, request.signal)
|
||||
const file = target.type === 'deliverables/presented' && isPresentedData(target.data) ? target.data.files[Number(index)] : undefined
|
||||
if (!isPresentedFile(file)) return new Response('Presented file not found in this Session result.', { status: 404 })
|
||||
const found = await ctx.sessionController.resolveAgent(id as SessionId)
|
||||
if ('error' in found) throw found.error
|
||||
request.signal.throwIfAborted()
|
||||
const { agent } = found
|
||||
const workspaceFiles = agent.ctx.get('workspaceFiles')
|
||||
const fs = agent.ctx.get('fs')
|
||||
if (workspaceFiles === undefined || fs === undefined) throw new Error('Session file services unavailable.')
|
||||
const { absolutePath: path } = await workspaceFiles.stat(agent, file.path, request.signal)
|
||||
const { fs, workspaceFiles } = ctx
|
||||
const { absolutePath: path } = await workspaceFiles.stat({
|
||||
sessionId: id as SessionId,
|
||||
workspaceRoot: session.cwd ?? ctx.sandboxPolicy.workspaceRoot,
|
||||
}, file.path, request.signal)
|
||||
const mapped = fs.processPathFromHostPath(path)
|
||||
if (mapped === undefined || fs.processPath(await fs.resolve(mapped, { signal: request.signal })) !== path) {
|
||||
return new Response('Presented file has no verified Host path.', { status: 422 })
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
import { mkdtemp, rm, readFile, writeFile, mkdir, realpath, symlink, unlink } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { LocalFileSystem } from '@deepseek-ai/dsh-fs-local'
|
||||
import { WorkspaceFiles } from '@deepseek-ai/dsh-api-workspace-files'
|
||||
import { RemoteError } from '@deepseek-ai/dsh-typert-protocol'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { HostConnectionService } from '@deepseek-ai/dsh-client-connection'
|
||||
import type { BrowserAuth } from '@deepseek-ai/dsh-client-connection/src/browser-auth.ts'
|
||||
@@ -35,10 +33,12 @@ async function fixture() {
|
||||
cleanups.push(() => ctx.fiber.dispose())
|
||||
const session: { cwd?: string } = { cwd }
|
||||
await ctx.plugin(LocalFileSystem, { cwd })
|
||||
ctx.provide('sandboxPolicy', { resolve: () => ({ workspaceRoot: session.cwd ?? cwd }) } as never)
|
||||
new WorkspaceFiles(ctx, { maxBytes: 1024, maxFileBytes: 1024, maxLines: 100, maxEntries: 100 })
|
||||
const agent = { ctx, session: { header: session } } as unknown as Agent
|
||||
const resolveAgent = vi.fn<Context['sessionController']['resolveAgent']>(async () => ({ agent }))
|
||||
ctx.provide('sandboxPolicy', { workspaceRoot: cwd } as never)
|
||||
await ctx.plugin({
|
||||
inject: ['fs', 'sandboxPolicy'],
|
||||
apply: (scope) => { new WorkspaceFiles(scope, { maxBytes: 1024, maxFileBytes: 1024, maxLines: 100, maxEntries: 100 }) },
|
||||
})
|
||||
const resolveAgent = vi.fn<Context['sessionController']['resolveAgent']>(() => { throw new Error('Agent activation is unavailable') })
|
||||
const readEvent = vi.fn(async (request: SessionEventReadRequest) => {
|
||||
if (request.sessionId !== 'owner') throw new SessionQueryError('missing', 'SESSION_QUERY_SESSION_NOT_FOUND')
|
||||
if (request.seq !== 7) throw new SessionQueryError('missing', 'SESSION_QUERY_EVENT_NOT_FOUND')
|
||||
@@ -48,7 +48,7 @@ async function fixture() {
|
||||
const opener = vi.fn(async (_request: { path: string; action?: 'reveal' }, _signal: AbortSignal) => ({ opened: true as const }))
|
||||
ctx.provide('sessionController', { resolveAgent, openWorkspacePath: opener, workspaceDesktop: () => ({ name: 'desktop', available: true, fileManager: 'finder' }) } as never)
|
||||
const connection = new HostConnectionService(ctx, [], {} as BrowserAuth)
|
||||
const fiber = ctx.plugin({ inject: ['connection', 'sessionQuery', 'sessionController', 'workspaceFiles', 'fs'], apply: registerPresentOpen })
|
||||
const fiber = ctx.plugin({ inject: ['connection', 'sessionQuery', 'sessionController', 'workspaceFiles', 'fs', 'sandboxPolicy'], apply: registerPresentOpen })
|
||||
await fiber
|
||||
const handler = connection.createSharedFetchHandler('/api')
|
||||
const open = (query = '?sessionId=owner&seq=7&index=0', signal?: AbortSignal) => handler.fetch(new Request(
|
||||
@@ -219,29 +219,17 @@ it('refuses native opening without a matching Host mapping even when a same-name
|
||||
expect(opener).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('reports Session resolution failures before checking files', async () => {
|
||||
const { resolveAgent, open, opener } = await fixture()
|
||||
resolveAgent.mockResolvedValueOnce({ error: new RemoteError('session/not-found', 'missing', { sessionId: SessionId('owner') }) })
|
||||
expect((await open()).status).toBe(404)
|
||||
expect(opener).not.toHaveBeenCalled()
|
||||
it('opens a viewed child Session without activating an Agent', async () => {
|
||||
const { session, readEvent, open, opener, resolveAgent } = await fixture()
|
||||
readEvent.mockResolvedValueOnce({ session, target: { type: 'deliverables/presented', data: { turn: 1, callId: 'child', files: [{ path: '日记模板.docx' }] } } as SessionEvent })
|
||||
expect((await open('?sessionId=child&seq=7&index=0')).status).toBe(204)
|
||||
expect(opener).toHaveBeenCalledOnce()
|
||||
expect(resolveAgent).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
|
||||
it('uses the resolved Agent filesystem instead of the Host filesystem', async () => {
|
||||
const { ctx, cwd, open, resolveAgent, opener } = await fixture()
|
||||
const scoped = ctx.isolate('fs')
|
||||
await scoped.plugin(LocalFileSystem, { cwd })
|
||||
vi.spyOn(scoped.fs, 'processPathFromHostPath').mockReturnValue(undefined)
|
||||
resolveAgent.mockResolvedValueOnce({ agent: { ctx: scoped, session: { header: { cwd } } } as unknown as Agent })
|
||||
expect((await open()).status).toBe(422)
|
||||
expect(opener).not.toHaveBeenCalled()
|
||||
it('uses the deployment workspace root when the viewed Session has no cwd', async () => {
|
||||
const { session, open, cwd, file, opener } = await fixture()
|
||||
delete session.cwd
|
||||
expect((await open()).status).toBe(204)
|
||||
})
|
||||
|
||||
|
||||
it.each(['workspaceFiles', 'fs'])('refuses an Agent missing its %s service', async (service) => {
|
||||
const { ctx, open, resolveAgent, opener } = await fixture()
|
||||
resolveAgent.mockResolvedValueOnce({ agent: { ctx: ctx.isolate(service) } as unknown as Agent })
|
||||
expect((await open()).status).toBe(500)
|
||||
expect(opener).not.toHaveBeenCalled()
|
||||
expect(opener.mock.lastCall?.[0].path).toBe(await realpath(join(cwd, file.path)))
|
||||
})
|
||||
|
||||
@@ -19,6 +19,9 @@ describe('ui-deliverables node plugin', () => {
|
||||
ctx.provide('connection', { fetch: { register: () => () => {} } } as never)
|
||||
ctx.provide('sessionQuery', {} as never)
|
||||
ctx.provide('sessionController', {} as never)
|
||||
ctx.provide('workspaceFiles', {} as never)
|
||||
ctx.provide('fs', {} as never)
|
||||
ctx.provide('sandboxPolicy', {} as never)
|
||||
const mounted = ctx.plugin({ apply, inject })
|
||||
await mounted.await()
|
||||
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
},
|
||||
{
|
||||
"path": "../../typert/protocol"
|
||||
},
|
||||
{
|
||||
"path": "../../sandbox/sandbox-policy"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Generated
+3
@@ -2742,6 +2742,9 @@ importers:
|
||||
'@deepseek-ai/dsh-llm':
|
||||
specifier: workspace:^
|
||||
version: link:../../llm/llm
|
||||
'@deepseek-ai/dsh-sandbox-policy':
|
||||
specifier: workspace:^
|
||||
version: link:../../sandbox/sandbox-policy
|
||||
'@deepseek-ai/dsh-session':
|
||||
specifier: workspace:^
|
||||
version: link:../../core/session
|
||||
|
||||
Reference in New Issue
Block a user