Merge remote-tracking branch 'origin/master' into feat/plugin-detail-slots

This commit is contained in:
Yichen Jiang committed 2026-09-21 17:15:10 +08:00
commit aba8b3a29f
259 files changed
+6650 -1951

No files matched your search

+4
View File
@@ -433,7 +433,9 @@ export const LINK_MAP: Readonly<Record<string, string>> = {
StoredImageAttachment: 'attachment.md',
ShellExecRequest: 'shell.md',
ShellExecSpec: 'shell.md',
ShellExecution: 'shell.md',
ShellProcess: 'shell.md',
ShellPromotionOffer: 'shell.md',
ShellRunResult: 'shell.md',
DshEnvironment: 'subprocess.md',
SubprocessHandle: 'subprocess.md',
@@ -588,6 +590,8 @@ export const LINK_MAP: Readonly<Record<string, string>> = {
SystemPrompt: 'system-prompt.md',
ToolProviderResult: 'system-prompt.md',
JobId: 'jobs.md',
JobKillRequest: 'jobs.md',
JobKillValue: 'jobs.md',
JobFollowFrame: 'jobs.md',
JobFollowRequest: 'jobs.md',
JobListFrame: 'jobs.md',
+9 -4
View File
@@ -286,16 +286,19 @@ const TOOL_PACKAGES: ToolPackage[] = [
pkg: '@deepseek-ai/dsh-tool-bash',
dir: 'tool-bash',
source: 'packages/shell/tool-bash/src/index.ts',
requires: ['ctx.tools', 'ctx.shell', 'ctx.systemPrompt', 'ctx.shellEnv', 'ctx.jobs at call time for run_in_background'],
requires: ['ctx.tools', 'ctx.shell', 'ctx.systemPrompt', 'ctx.shellEnv', 'ctx.jobs for run_in_background and the job-backed foreground path'],
writes: ['tool/call', 'tool/result'],
async mount(ctx) {
await ctx.plugin(LocalSubprocessRuntime)
await ctx.plugin(BashEnvPlugin)
await ctx.plugin(LocalBashExecutor)
// The shipped profiles compose the job registry, and the tool's
// background surface follows it: harvest the job-backed schema.
await ctx.plugin(LocalJobRegistry)
await ctx.plugin(ToolBash)
},
note:
'The bash tool is the model-facing consumer of the bash executor seam. A `run_in_background` run registers with the generic `ctx.jobs` runtime and is collected/stopped through the `job_*` tools from `@deepseek-ai/dsh-tool-jobs`; the `enableRunInBackground` config (default true) removes the parameter entirely when disabled.',
'The bash tool is the model-facing consumer of the bash executor seam. With a job registry composed every call registers with the generic `ctx.jobs` runtime as it starts, collected/stopped through the `job_*` tools from `@deepseek-ai/dsh-tool-jobs`; without one, or with `enableRunInBackground: false`, the tool registers a foreground-only schema without the `run_in_background` parameter.',
},
{
pkg: '@deepseek-ai/dsh-tool-present',
@@ -313,15 +316,17 @@ const TOOL_PACKAGES: ToolPackage[] = [
pkg: '@deepseek-ai/dsh-tool-pwsh',
dir: 'tool-pwsh',
source: 'packages/shell/tool-pwsh/src/index.ts',
requires: ['ctx.tools', 'ctx.shell', 'ctx.systemPrompt', 'ctx.shellEnv', 'ctx.jobs at call time for run_in_background'],
requires: ['ctx.tools', 'ctx.shell', 'ctx.systemPrompt', 'ctx.shellEnv', 'ctx.jobs for run_in_background and the job-backed foreground path'],
writes: ['tool/call', 'tool/result'],
async mount(ctx) {
// The pwsh tool consumes the bash executor seam; the schema harvest
// mounts the pwsh-local implementation so the inject resolves without
// executing anything (registration never spawns a process).
// executing anything (registration never spawns a process). The job
// registry is composed for the same reason as the bash entry.
await ctx.plugin(LocalSubprocessRuntime)
await ctx.plugin(BashEnvPlugin)
await ctx.plugin(PwshLocalExecutor)
await ctx.plugin(LocalJobRegistry)
await ctx.plugin(ToolPwsh)
},
note:
-3
View File
@@ -1388,9 +1388,6 @@
"packages/hooks/hook-protocol/tests/events.spec.ts": {
"a59fe57e5f3994018a6bd25208b9c2f56a7bcf7353d6ec2d453564f3f5c225f2": 1
},
"packages/hooks/hook-protocol/tests/runner.spec.ts": {
"09500884210bc2ec2161286a539ca63f8c2627ca73a82ca00d24c58d600118f3": 1
},
"packages/hooks/hooks-claude-code/tests/bridge.spec.ts": {
"38a7cf3dfd5da2f739390f5b233b97731f3bfa1ac3a51fdb8e1594735764bb80": 1
},
+5 -5
View File
@@ -40,24 +40,24 @@ it.skipIf(process.platform === 'win32')('resolves the exact spill verifier, reta
await adapter
const command = 'file="' + saved.locator + '"' + "; grep -Fq request/header \"$file\" && grep -Fq session_event_search \"$file\" && printf 'SPILL_CANONICAL_OK\\n'"
const spec = ctx.shell.resolve({ command })
const output = await ctx.shell.run(spec)
const output = await (await ctx.shell.execute(spec)).result()
expect(output.timedOut).toBe(false)
expect(output.exitCode).toBe(0)
expect(output.stdout.text).toBe('SPILL_CANONICAL_OK\n')
expect(spec.command).toBe(command)
const other = await ctx.shell.run(ctx.shell.resolve({ command: 'printf ordinary; exit 7' }))
const other = await (await ctx.shell.execute(ctx.shell.resolve({ command: 'printf ordinary; exit 7' }))).result()
expect(other.exitCode).toBe(7)
expect(other.stdout.text).toBe('ordinary')
const unmatched = await ctx.shell.run(ctx.shell.resolve({ command: command + '; exit 9' }))
const unmatched = await (await ctx.shell.execute(ctx.shell.resolve({ command: command + '; exit 9' }))).result()
expect(unmatched.exitCode).toBe(9)
expect(unmatched.stderr.text).toContain('No such file')
const physical = ctx.fs.processPath(await ctx.fs.resolve(saved.locator))
await rm(physical)
const missing = await ctx.shell.run(spec)
const missing = await (await ctx.shell.execute(spec)).result()
expect(missing.exitCode).not.toBe(0)
expect(missing.stdout.text).toBe('')
await adapter.dispose()
const restored = await ctx.shell.run(spec)
const restored = await (await ctx.shell.execute(spec)).result()
expect(restored.exitCode).not.toBe(0)
expect(restored.stderr.text).toContain(saved.locator)
} finally {
+3 -3
View File
@@ -23,16 +23,16 @@ it.skipIf(process.platform === 'win32')('translates only the exact fixture comma
const fork = ctx.plugin(adapter, { command, recordedPath: '/fixture/recorded.txt', livePath })
disposers.push(() => fork.dispose())
await fork
const outcome = await ctx.shell.run(ctx.shell.resolve({ command }))
const outcome = await (await ctx.shell.execute(ctx.shell.resolve({ command }))).result()
expect(outcome.timedOut).toBe(false)
expect(outcome.exitCode).toBe(0)
expect(await readFile(livePath, 'utf8')).toBe('actual bytes')
const untouched = await ctx.shell.run(ctx.shell.resolve({ command: "printf '/fixture/recorded.txt'; exit 7" }))
const untouched = await (await ctx.shell.execute(ctx.shell.resolve({ command: "printf '/fixture/recorded.txt'; exit 7" }))).result()
expect(untouched.exitCode).toBe(7)
expect(untouched.stdout.text).toBe('/fixture/recorded.txt')
await fork.dispose()
await rm(livePath)
const restored = await ctx.shell.run(ctx.shell.resolve({ command }))
const restored = await (await ctx.shell.execute(ctx.shell.resolve({ command }))).result()
expect(restored.exitCode).not.toBe(0)
await expect(readFile(livePath)).rejects.toMatchObject({ code: 'ENOENT' })
} finally {
+3 -3
View File
@@ -20,11 +20,11 @@ export interface Config {
export function apply(ctx: Context, config: Config): void {
const shell = ctx.shell
// oxlint-disable-next-line typescript/unbound-method -- preserve method identity for restoration; calls bind the receiver.
const run = shell.run
const execute = shell.execute
ctx.effect(() => {
shell.run = spec => run.call(shell, spec.command === config.command
shell.execute = spec => execute.call(shell, spec.command === config.command
? { ...spec, command: spec.command.replaceAll(config.recordedPath, "'" + config.livePath.replaceAll("'", "'\"'\"'") + "'") }
: spec)
return () => { shell.run = run }
return () => { shell.execute = execute }
})
}
+4
View File
@@ -44,15 +44,19 @@ it.each([false, true])('keeps concurrent physical spill files private while reta
expect(target.displayPath).toBe(ref.locator)
const physicalPath = ctx.fs.processPath(target)
expect(physicalPath.startsWith(await realpath(root))).toBe(true)
expect(await realpath(ctx.fs.processPathFromHostPath(ref.locator)!)).toBe(physicalPath)
expect(await readFile(physicalPath, 'utf8')).toBe(content)
expect(await ctx.fs.readText(target)).toBe(content)
await expect(ctx.fs.resolve(join(locatorRoot, 'missing.txt'))).rejects.toThrow('not saved by this run')
expect(() => ctx.fs.processPathFromHostPath(join(locatorRoot, 'missing.txt'))).toThrow('not saved by this run')
const ordinary = join(root, 'ordinary.txt')
await writeFile(ordinary, 'ordinary')
expect(ctx.fs.processPathFromHostPath(ordinary)).toBe(ordinary)
expect(await ctx.fs.readText(await ctx.fs.resolve(ordinary))).toBe('ordinary')
await fork.dispose()
const restored = await ctx.fs.resolve(ref.locator)
expect(ctx.fs.processPath(restored)).not.toBe(physicalPath)
expect(ctx.fs.processPathFromHostPath(ref.locator)).toBe(ref.locator)
expect(await ctx.fs.stat(restored)).toBeUndefined()
return { root, physicalPath, locator: ref.locator, content }
}))
+17 -10
View File
@@ -1,4 +1,4 @@
/** Fixture-only logical locators over real local spill files; preview budgets retain recorded path lengths. */
/** Fixture-only logical locators for filesystem and subprocess reads of real spill files; preview budgets retain recorded path lengths. */
import type { Context } from '@deepseek-ai/cordis'
import { join, relative, resolve, sep } from 'node:path'
import type { SpillLocator } from '@deepseek-ai/dsh-spill'
@@ -28,6 +28,17 @@ export function apply(ctx: Context, config: Config): void {
const saveText = store.saveText
// oxlint-disable-next-line typescript/unbound-method -- preserve method identity for restoration; calls bind the receiver.
const resolvePath = fs.resolve
// oxlint-disable-next-line typescript/unbound-method -- preserve method identity for restoration; calls bind the receiver.
const processPathFromHostPath = fs.processPathFromHostPath
const mappedPath = (path: string): string => {
const live = paths.get(path)
if (live !== undefined) return live
if (path.startsWith(locatorRoot + sep)) {
throw new Error('snapshot spill locator was not saved by this run')
}
return path
}
ctx.effect(() => {
store.saveText = async (input) => {
const saved = await saveText.call(store, input)
@@ -40,19 +51,15 @@ export function apply(ctx: Context, config: Config): void {
return { ...saved, locator }
}
fs.resolve = async (path, opts) => {
const live = paths.get(path)
if (live !== undefined) {
const target = await resolvePath.call(fs, live, opts)
return { ...target, displayPath: path }
}
if (path.startsWith(locatorRoot + sep)) {
throw new Error('snapshot spill locator was not saved by this run')
}
return resolvePath.call(fs, path, opts)
const live = mappedPath(path)
const target = await resolvePath.call(fs, live, opts)
return live === path ? target : { ...target, displayPath: path }
}
fs.processPathFromHostPath = path => processPathFromHostPath.call(fs, mappedPath(path))
return () => {
store.saveText = saveText
fs.resolve = resolvePath
fs.processPathFromHostPath = processPathFromHostPath
paths.clear()
}
})