mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
Merge branch 'stack/agent-profiles-3-wire' into stack/agent-profiles-4-settings
This commit is contained in:
@@ -15,20 +15,12 @@
|
|||||||
text: >-
|
text: >-
|
||||||
You are a coding agent powered by the {{model}} model. Your working directory is {{cwd}}.
|
You are a coding agent powered by the {{model}} model. Your working directory is {{cwd}}.
|
||||||
|
|
||||||
# `tool-bash` provides the `bashEnv` service, so it needs a realm even alone.
|
# `bash-env` stays in the HOST composition: `apps/cli/src/web.ts` injects it to
|
||||||
- id: shell
|
# publish `DSH_WEB_URL`/`DSH_WEB_MODE`, and a host row that injects a service is
|
||||||
name: cordis:group
|
# the criterion for host-plane ownership. `tool-bash` consumes that host
|
||||||
group: true
|
# registry from here.
|
||||||
isolate:
|
- id: tool-bash
|
||||||
bashEnv: true
|
name: '@deepseek-ai/dsh-tool-bash'
|
||||||
config:
|
|
||||||
# The registry and its consumer share the realm: a consumer left outside
|
|
||||||
# would resolve the host's `bashEnv`, which this plane no longer provides.
|
|
||||||
- id: bash-env
|
|
||||||
name: '@deepseek-ai/dsh-bash-env'
|
|
||||||
|
|
||||||
- id: tool-bash
|
|
||||||
name: '@deepseek-ai/dsh-tool-bash'
|
|
||||||
|
|
||||||
- id: tool-str-replace-editor
|
- id: tool-str-replace-editor
|
||||||
name: '@deepseek-ai/dsh-tool-str-replace-editor'
|
name: '@deepseek-ai/dsh-tool-str-replace-editor'
|
||||||
|
|||||||
@@ -31,22 +31,15 @@
|
|||||||
|
|
||||||
# ── shell ───────────────────────────────────────────────────────────────────
|
# ── shell ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
# `tool-bash` reads as a tool but provides the `bashEnv` service, so it needs a
|
# `bash-env` stays in the HOST composition: `apps/cli/src/web.ts` injects it to
|
||||||
# realm like any other provider. The executor behind it (`bash-sandbox`) stays
|
# publish `DSH_WEB_URL`/`DSH_WEB_MODE`, and a host row that injects a service is
|
||||||
# in the host composition, where the sandbox policy owns it.
|
# the criterion for host-plane ownership — injection resolves before any session
|
||||||
- id: shell
|
# exists, so there is no agent to key by. Behind a preset realm those variables
|
||||||
name: cordis:group
|
# never reached the model's shell at all. `tool-bash` consumes the host registry
|
||||||
group: true
|
# from here; the executor behind it (`bash-sandbox`) is host-plane too, where the
|
||||||
isolate:
|
# sandbox policy owns it.
|
||||||
bashEnv: true
|
- id: tool-bash
|
||||||
config:
|
name: '@deepseek-ai/dsh-tool-bash'
|
||||||
# The registry and its consumer share the realm: a consumer left outside
|
|
||||||
# would resolve the host's `bashEnv`, which this plane no longer provides.
|
|
||||||
- id: bash-env
|
|
||||||
name: '@deepseek-ai/dsh-bash-env'
|
|
||||||
|
|
||||||
- id: tool-bash
|
|
||||||
name: '@deepseek-ai/dsh-tool-bash'
|
|
||||||
|
|
||||||
# ── filesystem ──────────────────────────────────────────────────────────────
|
# ── filesystem ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -172,29 +165,21 @@
|
|||||||
|
|
||||||
# ── delegation and workflows ────────────────────────────────────────────────
|
# ── delegation and workflows ────────────────────────────────────────────────
|
||||||
|
|
||||||
# Every backend and every tool that reaches `subagents` or `workflows` shares
|
# The `subagents` registry and its spawn/fork backends live in the HOST
|
||||||
# one realm: a consumer left outside it would resolve the host's registry
|
# composition: the registry is a process singleton whose cross-session queries
|
||||||
# instead, which this preset does not populate.
|
# the api-proxy serves to the browser, and a provider name may only be
|
||||||
|
# registered once. This preset contributes the delegation TOOLS, which resolve
|
||||||
|
# that host registry.
|
||||||
|
#
|
||||||
|
# `workflows` is different — nothing outside an agent reads it — so every row
|
||||||
|
# that reaches it shares one entry-local realm here, and a consumer left
|
||||||
|
# outside would resolve a host registry this preset does not populate.
|
||||||
- id: delegation
|
- id: delegation
|
||||||
name: cordis:group
|
name: cordis:group
|
||||||
group: true
|
group: true
|
||||||
isolate:
|
isolate:
|
||||||
subagents: true
|
|
||||||
workflows: true
|
workflows: true
|
||||||
config:
|
config:
|
||||||
- id: subagent
|
|
||||||
name: '@deepseek-ai/dsh-subagent'
|
|
||||||
|
|
||||||
- id: subagent-spawn
|
|
||||||
name: '@deepseek-ai/dsh-subagent-spawn'
|
|
||||||
config:
|
|
||||||
providerName: spawn
|
|
||||||
|
|
||||||
- id: subagent-fork
|
|
||||||
name: '@deepseek-ai/dsh-subagent-fork'
|
|
||||||
config:
|
|
||||||
providerName: fork
|
|
||||||
|
|
||||||
- id: tool-subagent-control
|
- id: tool-subagent-control
|
||||||
name: '@deepseek-ai/dsh-tool-subagent-control'
|
name: '@deepseek-ai/dsh-tool-subagent-control'
|
||||||
|
|
||||||
@@ -215,8 +200,11 @@
|
|||||||
toolName: subagent_fork
|
toolName: subagent_fork
|
||||||
backgroundMode: continuable
|
backgroundMode: continuable
|
||||||
|
|
||||||
- id: tool-subagent-report
|
# `tool-subagent-report` is host-plane for the same reason as the registry,
|
||||||
name: '@deepseek-ai/dsh-tool-subagent-report'
|
# not because a preset may not want it: it registers a CONTINUABLE SETUP on
|
||||||
|
# that singleton rather than a tool this agent calls, and the setup list is
|
||||||
|
# not scope-aware — one copy per mounted preset means every child gets
|
||||||
|
# `report` registered once per live session, which throws on the second.
|
||||||
|
|
||||||
- id: workflow-workerthread
|
- id: workflow-workerthread
|
||||||
name: '@deepseek-ai/dsh-workflow-workerthread'
|
name: '@deepseek-ai/dsh-workflow-workerthread'
|
||||||
@@ -234,6 +222,9 @@
|
|||||||
|
|
||||||
# ── remaining model-facing rows ─────────────────────────────────────────────
|
# ── remaining model-facing rows ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
- id: tool-ask-user
|
||||||
|
name: '@deepseek-ai/dsh-tool-ask-user'
|
||||||
|
|
||||||
- id: tool-todo
|
- id: tool-todo
|
||||||
name: '@deepseek-ai/dsh-tool-todo'
|
name: '@deepseek-ai/dsh-tool-todo'
|
||||||
|
|
||||||
|
|||||||
@@ -19,17 +19,44 @@
|
|||||||
"@cordisjs/plugin-timer": "workspace:*",
|
"@cordisjs/plugin-timer": "workspace:*",
|
||||||
"@deepseek-ai/dsh-app-boot": "workspace:^",
|
"@deepseek-ai/dsh-app-boot": "workspace:^",
|
||||||
"@deepseek-ai/dsh-base": "workspace:^",
|
"@deepseek-ai/dsh-base": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-command-compact": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-command-goal": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-compact-basic": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-compact-tool-result-prune": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-goal": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-goal-session": "workspace:^",
|
||||||
"@deepseek-ai/dsh-headless": "workspace:^",
|
"@deepseek-ai/dsh-headless": "workspace:^",
|
||||||
"@deepseek-ai/dsh-mcp-client": "workspace:^",
|
"@deepseek-ai/dsh-mcp-client": "workspace:^",
|
||||||
"@deepseek-ai/dsh-paths": "workspace:^",
|
"@deepseek-ai/dsh-paths": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-persona": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-plan-mode": "workspace:^",
|
||||||
"@deepseek-ai/dsh-pty": "workspace:^",
|
"@deepseek-ai/dsh-pty": "workspace:^",
|
||||||
"@deepseek-ai/dsh-pty-local": "workspace:^",
|
"@deepseek-ai/dsh-pty-local": "workspace:^",
|
||||||
"@deepseek-ai/dsh-session-reference": "workspace:^",
|
"@deepseek-ai/dsh-session-reference": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-skill": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-skill-local": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-tasks-local": "workspace:^",
|
||||||
"@deepseek-ai/dsh-tmux-context": "workspace:^",
|
"@deepseek-ai/dsh-tmux-context": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-token-meter": "workspace:^",
|
||||||
"@deepseek-ai/dsh-tool-ask-user": "workspace:^",
|
"@deepseek-ai/dsh-tool-ask-user": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-tool-bash": "workspace:^",
|
||||||
"@deepseek-ai/dsh-tool-bash-persistent": "workspace:^",
|
"@deepseek-ai/dsh-tool-bash-persistent": "workspace:^",
|
||||||
"@deepseek-ai/dsh-tool-cordis": "workspace:^",
|
"@deepseek-ai/dsh-tool-cordis": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-tool-fs": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-tool-fs-search": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-tool-goal": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-tool-ralph": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-tool-skill": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-tool-str-replace-editor": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-tool-subagent": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-tool-subagent-control": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-tool-tasks": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-tool-todo": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-tool-web": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-tool-workflow": "workspace:^",
|
||||||
"@deepseek-ai/dsh-web-app": "workspace:^",
|
"@deepseek-ai/dsh-web-app": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-workflow-workerthread": "workspace:^",
|
||||||
|
"@deepseek-ai/dsh-workspace-context": "workspace:^",
|
||||||
"commander": "^15.0.0",
|
"commander": "^15.0.0",
|
||||||
"cordis": "^4.0.0-rc.7",
|
"cordis": "^4.0.0-rc.7",
|
||||||
"js-yaml": "^4.2.0",
|
"js-yaml": "^4.2.0",
|
||||||
|
|||||||
@@ -66,11 +66,13 @@ beforeAll(async () => {
|
|||||||
}, 120_000)
|
}, 120_000)
|
||||||
|
|
||||||
describe('the shipped Web composition', () => {
|
describe('the shipped Web composition', () => {
|
||||||
it('leaves only the host UI tool in the global layer', () => {
|
it('leaves the global tool layer empty', () => {
|
||||||
// `ask_user_question` is the host's own interaction surface, not an agent
|
// Every model-facing tool belongs to a preset, `ask_user_question`
|
||||||
// capability, so it stays global. Every other tool now belongs to a
|
// included: a tool in the global layer reaches EVERY agent regardless of
|
||||||
// preset; a regression here means an agent-plane row came back to base.
|
// which preset composed it, so a two-tool benchmark surface would really
|
||||||
expect(toolNames(ctx)).toEqual(['ask_user_question'])
|
// present three. A regression here means an agent-plane row came back to
|
||||||
|
// the host composition.
|
||||||
|
expect(toolNames(ctx)).toEqual([])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('supplies both shipped presets, and only those, from the system root', async () => {
|
it('supplies both shipped presets, and only those, from the system root', async () => {
|
||||||
@@ -110,7 +112,8 @@ describe('the shipped Web composition', () => {
|
|||||||
setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'core-web').then(() => undefined),
|
setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'core-web').then(() => undefined),
|
||||||
})
|
})
|
||||||
try {
|
try {
|
||||||
expect(toolNames(ctx, handle.agent)).toEqual(['ask_user_question', 'bash', 'str_replace_editor'])
|
// Exactly what the preset lists — nothing arrives from the host.
|
||||||
|
expect(toolNames(ctx, handle.agent)).toEqual(['bash', 'str_replace_editor'])
|
||||||
} finally {
|
} finally {
|
||||||
await handle.dispose()
|
await handle.dispose()
|
||||||
}
|
}
|
||||||
@@ -126,14 +129,14 @@ describe('the shipped Web composition', () => {
|
|||||||
setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'core-web').then(() => undefined),
|
setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'core-web').then(() => undefined),
|
||||||
})
|
})
|
||||||
try {
|
try {
|
||||||
expect(toolNames(ctx, minimal.agent)).toEqual(['ask_user_question', 'bash', 'str_replace_editor'])
|
expect(toolNames(ctx, minimal.agent)).toEqual(['bash', 'str_replace_editor'])
|
||||||
expect(toolNames(ctx, full.agent).length).toBeGreaterThan(10)
|
expect(toolNames(ctx, full.agent).length).toBeGreaterThan(10)
|
||||||
|
|
||||||
await minimal.dispose()
|
await minimal.dispose()
|
||||||
|
|
||||||
// Tearing the minimal session down leaves the full one whole.
|
// Tearing the minimal session down leaves the full one whole.
|
||||||
expect(toolNames(ctx, full.agent).length).toBeGreaterThan(10)
|
expect(toolNames(ctx, full.agent).length).toBeGreaterThan(10)
|
||||||
expect(toolNames(ctx)).toEqual(['ask_user_question'])
|
expect(toolNames(ctx)).toEqual([])
|
||||||
} finally {
|
} finally {
|
||||||
await full.dispose()
|
await full.dispose()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { tmpdir } from 'node:os'
|
|||||||
import { join } from 'node:path'
|
import { join } from 'node:path'
|
||||||
import { expect, it } from 'vitest'
|
import { expect, it } from 'vitest'
|
||||||
import type {} from '@deepseek-ai/dsh-skill'
|
import type {} from '@deepseek-ai/dsh-skill'
|
||||||
|
import { SessionId } from '@deepseek-ai/dsh-session'
|
||||||
|
import type {} from '@deepseek-ai/dsh-agent-presets'
|
||||||
import { launchWebScaffold, type WebScaffold } from './scaffold.ts'
|
import { launchWebScaffold, type WebScaffold } from './scaffold.ts'
|
||||||
|
|
||||||
async function writeSkill(root: string, name: string): Promise<void> {
|
async function writeSkill(root: string, name: string): Promise<void> {
|
||||||
@@ -37,10 +39,26 @@ it('isolates replay skill discovery from every ambient host root', async () => {
|
|||||||
let scaffold: WebScaffold | undefined
|
let scaffold: WebScaffold | undefined
|
||||||
try {
|
try {
|
||||||
scaffold = await launchWebScaffold()
|
scaffold = await launchWebScaffold()
|
||||||
const names = (await scaffold.ctx.skills.list({ cwd: scaffold.workspaceCwd })).map(skill => skill.name)
|
const ctx = scaffold.ctx
|
||||||
expect(names).not.toContain('ambient-dsh')
|
// The skill registry belongs to one agent's preset, behind an `isolate`
|
||||||
expect(names).not.toContain('ambient-agents')
|
// realm the host cannot resolve by name — so the roots under test are only
|
||||||
expect(names).not.toContain('ambient-bundled')
|
// reachable through a composed agent, which is also the only shape that
|
||||||
|
// ever asks. `serviceFor` is the same addressing the gateway's `skill.list`
|
||||||
|
// uses for a browser request about a session.
|
||||||
|
const handle = await ctx.agents.create({
|
||||||
|
sessionId: SessionId('hermetic-skills'),
|
||||||
|
setup: agentCtx => ctx.agentPresets.mount(agentCtx).then(() => undefined),
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
const skills = ctx.agentPresets.serviceFor(handle.agent, 'skills')
|
||||||
|
if (skills === undefined) throw new Error('composed agent mounts no skill registry')
|
||||||
|
const names = (await skills.list({ cwd: scaffold.workspaceCwd })).map(skill => skill.name)
|
||||||
|
expect(names).not.toContain('ambient-dsh')
|
||||||
|
expect(names).not.toContain('ambient-agents')
|
||||||
|
expect(names).not.toContain('ambient-bundled')
|
||||||
|
} finally {
|
||||||
|
await handle.dispose()
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
await scaffold?.close()
|
await scaffold?.close()
|
||||||
|
|||||||
@@ -240,6 +240,31 @@ export async function launchWebScaffold(options: LaunchOptions = {}): Promise<We
|
|||||||
// paths at load, and an in-process boot must NEVER touch the developer's
|
// paths at load, and an in-process boot must NEVER touch the developer's
|
||||||
// real ~/.dsh document or credential file.
|
// real ~/.dsh document or credential file.
|
||||||
const harnessHome = join(workspaceCwd, '.dsh-home')
|
const harnessHome = join(workspaceCwd, '.dsh-home')
|
||||||
|
// Skill discovery is model-visible input, and its roots now resolve inside a
|
||||||
|
// PRESET — a subtree this lane's include patches cannot reach, because the
|
||||||
|
// roster mounts it directly per session rather than as a row of the booted
|
||||||
|
// tree. The row's documented fallback is the environment, so pin that: the
|
||||||
|
// whole scaffold lifetime, not just the boot, since presets mount when a
|
||||||
|
// session is created. Without this a developer's real ~/.dsh/skills silently
|
||||||
|
// enters replay requests and goldens while CI sees none.
|
||||||
|
const skillRootEnvironment = {
|
||||||
|
DSH_HOME: join(workspaceCwd, '.dsh-home'),
|
||||||
|
DSH_AGENTS_HOME: join(workspaceCwd, '.agents-home'),
|
||||||
|
DSH_BUNDLED_SKILL_DIR: join(workspaceCwd, '.bundled-skills'),
|
||||||
|
}
|
||||||
|
const originalSkillRootEnvironment = Object.fromEntries(
|
||||||
|
Object.keys(skillRootEnvironment).map(key => [key, process.env[key]]),
|
||||||
|
)
|
||||||
|
let skillRootEnvironmentRestored = false
|
||||||
|
const restoreSkillRootEnvironment = (): void => {
|
||||||
|
if (skillRootEnvironmentRestored) return
|
||||||
|
skillRootEnvironmentRestored = true
|
||||||
|
for (const [key, value] of Object.entries(originalSkillRootEnvironment)) {
|
||||||
|
if (value === undefined) Reflect.deleteProperty(process.env, key)
|
||||||
|
else process.env[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Object.assign(process.env, skillRootEnvironment)
|
||||||
let persistenceRoot: string
|
let persistenceRoot: string
|
||||||
try {
|
try {
|
||||||
persistenceRoot = await mkdtemp(join(tmpdir(), 'dsh-web-e2e-sessions-'))
|
persistenceRoot = await mkdtemp(join(tmpdir(), 'dsh-web-e2e-sessions-'))
|
||||||
@@ -415,6 +440,7 @@ export async function launchWebScaffold(options: LaunchOptions = {}): Promise<We
|
|||||||
if (process.cwd() !== originalCwd) process.chdir(originalCwd)
|
if (process.cwd() !== originalCwd) process.chdir(originalCwd)
|
||||||
const cleanupFailures = await cleanupScaffoldWorld(ctx, workspaceCwd, persistenceRoot)
|
const cleanupFailures = await cleanupScaffoldWorld(ctx, workspaceCwd, persistenceRoot)
|
||||||
restoreCredentialEnvironment()
|
restoreCredentialEnvironment()
|
||||||
|
restoreSkillRootEnvironment()
|
||||||
if (cleanupFailures.length > 0) {
|
if (cleanupFailures.length > 0) {
|
||||||
throw new AggregateError([error, ...cleanupFailures], 'web scaffold setup failed and cleanup was incomplete')
|
throw new AggregateError([error, ...cleanupFailures], 'web scaffold setup failed and cleanup was incomplete')
|
||||||
}
|
}
|
||||||
@@ -462,6 +488,7 @@ export async function launchWebScaffold(options: LaunchOptions = {}): Promise<We
|
|||||||
failures.push(...await cleanupScaffoldWorld(ctx, workspaceCwd, persistenceRoot))
|
failures.push(...await cleanupScaffoldWorld(ctx, workspaceCwd, persistenceRoot))
|
||||||
} finally {
|
} finally {
|
||||||
restoreCredentialEnvironment()
|
restoreCredentialEnvironment()
|
||||||
|
restoreSkillRootEnvironment()
|
||||||
}
|
}
|
||||||
if (failures.length > 0) throw new AggregateError(failures, 'web scaffold teardown failed')
|
if (failures.length > 0) throw new AggregateError(failures, 'web scaffold teardown failed')
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
|
|||||||
import { createUserMessage } from '@deepseek-ai/dsh-llm'
|
import { createUserMessage } from '@deepseek-ai/dsh-llm'
|
||||||
import type { ContentBlock, Message } from '@deepseek-ai/dsh-llm'
|
import type { ContentBlock, Message } from '@deepseek-ai/dsh-llm'
|
||||||
import { deriveEventMessage, SessionId } from '@deepseek-ai/dsh-session'
|
import { deriveEventMessage, SessionId } from '@deepseek-ai/dsh-session'
|
||||||
|
import type {} from '@deepseek-ai/dsh-agent-presets'
|
||||||
import type { SessionEvent } from '@deepseek-ai/dsh-session'
|
import type { SessionEvent } from '@deepseek-ai/dsh-session'
|
||||||
import type { TokenMeterService } from '@deepseek-ai/dsh-token-meter'
|
import type { TokenMeterService } from '@deepseek-ai/dsh-token-meter'
|
||||||
import { join } from 'node:path'
|
import { join } from 'node:path'
|
||||||
@@ -170,10 +171,22 @@ describe('web e2e: seeded history renders through cold resume', () => {
|
|||||||
if (MODE !== 'record') {
|
if (MODE !== 'record') {
|
||||||
const raw = await readFile(SEED, 'utf8')
|
const raw = await readFile(SEED, 'utf8')
|
||||||
expect(fixtureUserPrompts(raw), 'seed fixture must carry exactly the drive prompt').toEqual([PROMPT])
|
expect(fixtureUserPrompts(raw), 'seed fixture must carry exactly the drive prompt').toEqual([PROMPT])
|
||||||
const meter = scaffold.ctx.get('tokenMeter')
|
// The meter belongs to an agent's preset, not to the process — token
|
||||||
if (meter === undefined) throw new Error('seeded-history requires the composed token meter')
|
// accounting is per session. It is used here as a pure pricing function
|
||||||
const realized = realizeSeedFixture(scaffold, raw, SEED_ID)
|
// over fixture content, so a throwaway composition is enough to reach one.
|
||||||
await seedSession(scaffold, withCompaction(realized, meter), SEED_ID)
|
const priced = await scaffold.ctx.agents.create({
|
||||||
|
sessionId: SessionId('seeded-history-pricing'),
|
||||||
|
setup: agentCtx => scaffold.ctx.agentPresets.mount(agentCtx).then(() => undefined),
|
||||||
|
})
|
||||||
|
let realizedWithCompaction: string
|
||||||
|
try {
|
||||||
|
const meter = scaffold.ctx.agentPresets.serviceFor(priced.agent, 'tokenMeter')
|
||||||
|
if (meter === undefined) throw new Error('seeded-history requires the composed token meter')
|
||||||
|
realizedWithCompaction = withCompaction(realizeSeedFixture(scaffold, raw, SEED_ID), meter)
|
||||||
|
} finally {
|
||||||
|
await priced.dispose()
|
||||||
|
}
|
||||||
|
await seedSession(scaffold, realizedWithCompaction, SEED_ID)
|
||||||
}
|
}
|
||||||
browser = await chromium.launch()
|
browser = await chromium.launch()
|
||||||
page = await newEnglishPage(browser)
|
page = await newEnglishPage(browser)
|
||||||
@@ -220,11 +233,17 @@ describe('web e2e: seeded history renders through cold resume', () => {
|
|||||||
const projections = body.result.value?.projections
|
const projections = body.result.value?.projections
|
||||||
expect(projections).toBeDefined()
|
expect(projections).toBeDefined()
|
||||||
expect(projections?.asOfSeq).toBeGreaterThanOrEqual(0)
|
expect(projections?.asOfSeq).toBeGreaterThanOrEqual(0)
|
||||||
// The seed carries a session/title event: the title unit must serve it.
|
// The seed carries a session/title event: the title unit is host-plane, so
|
||||||
|
// it folds the detached log and serves the value with nothing composed.
|
||||||
expect(typeof projections?.values.title).toBe('string')
|
expect(typeof projections?.values.title).toBe('string')
|
||||||
// tool-todo is composed but the seed has no todo/write: whole-value null,
|
// `todos` is NOT here, and that is the contract rather than a gap. Its unit
|
||||||
// key PRESENT (absence would mean the unit never registered).
|
// is registered by `tool-todo` inside an agent's preset, so a detached
|
||||||
expect(projections?.values).toHaveProperty('todos', null)
|
// session yields it from exactly one place: a durable checkpoint written
|
||||||
|
// while the session was live. This seed was written straight to persistence
|
||||||
|
// and never ran, so it recorded none — and the answer no longer depends on
|
||||||
|
// whether some UNRELATED session happens to be composed right now, which is
|
||||||
|
// the whole reason the checkpoint row carries its own view.
|
||||||
|
expect(projections?.values).not.toHaveProperty('todos')
|
||||||
})
|
})
|
||||||
|
|
||||||
it.skipIf(MODE === 'record')('lists the seeded session cold and renders its history from the log', async () => {
|
it.skipIf(MODE === 'record')('lists the seeded session cold and renders its history from the log', async () => {
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import type {} from '@deepseek-ai/dsh-tools'
|
|||||||
import type {} from '@deepseek-ai/dsh-sandbox-policy'
|
import type {} from '@deepseek-ai/dsh-sandbox-policy'
|
||||||
import type {} from '@deepseek-ai/dsh-user-approval'
|
import type {} from '@deepseek-ai/dsh-user-approval'
|
||||||
import type {} from '@deepseek-ai/dsh-permission'
|
import type {} from '@deepseek-ai/dsh-permission'
|
||||||
|
import { SessionId } from '@deepseek-ai/dsh-session'
|
||||||
|
import type {} from '@deepseek-ai/dsh-agent-presets'
|
||||||
import { launchWebScaffold, type WebScaffold } from './scaffold.ts'
|
import { launchWebScaffold, type WebScaffold } from './scaffold.ts'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,11 +65,26 @@ afterEach(async () => {
|
|||||||
|
|
||||||
it('assembles the shipped Web catalog with the confined access default', async () => {
|
it('assembles the shipped Web catalog with the confined access default', async () => {
|
||||||
scaffold = await launchWebScaffold()
|
scaffold = await launchWebScaffold()
|
||||||
const names = scaffold.ctx.tools.schemas().map(schema => schema.name).sort()
|
const ctx = scaffold.ctx
|
||||||
expect(names.filter(name => !RIPGREP_TOOLS.includes(name))).toEqual(EXPECTED_TOOLS)
|
// The catalog belongs to an AGENT, not to the process: every model-facing row
|
||||||
// The packaged ripgrep binary ships with the dependency, so the pair is a
|
// now lives in a preset mounted under one session's scope, so the global
|
||||||
// fixed roster member on every host.
|
// layer holds nothing and a caller must name the agent to see anything. This
|
||||||
expect(names.filter(name => RIPGREP_TOOLS.includes(name))).toEqual(RIPGREP_TOOLS)
|
// composes from the deployment default — what a session that names no preset
|
||||||
|
// gets — which is the shape this test has always been about.
|
||||||
|
expect(ctx.tools.schemas().map(schema => schema.name)).toEqual([])
|
||||||
|
const handle = await ctx.agents.create({
|
||||||
|
sessionId: SessionId('shipped-composition'),
|
||||||
|
setup: agentCtx => ctx.agentPresets.mount(agentCtx).then(() => undefined),
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
const names = ctx.tools.schemas(handle.agent).map(schema => schema.name).sort()
|
||||||
|
expect(names.filter(name => !RIPGREP_TOOLS.includes(name))).toEqual(EXPECTED_TOOLS)
|
||||||
|
// The packaged ripgrep binary ships with the dependency, so the pair is a
|
||||||
|
// fixed roster member on every host.
|
||||||
|
expect(names.filter(name => RIPGREP_TOOLS.includes(name))).toEqual(RIPGREP_TOOLS)
|
||||||
|
} finally {
|
||||||
|
await handle.dispose()
|
||||||
|
}
|
||||||
// `workspace-write` is not "the workspace and nothing else": the shared roots
|
// `workspace-write` is not "the workspace and nothing else": the shared roots
|
||||||
// helper always admits the temp directories too. Pinning it against an
|
// helper always admits the temp directories too. Pinning it against an
|
||||||
// explicit mode keeps the claim independent of this surface's default, and
|
// explicit mode keeps the claim independent of this surface's default, and
|
||||||
|
|||||||
@@ -2529,7 +2529,7 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co
|
|||||||
- `@deepseek-ai/dsh-client-ui-models` ([`packages/client/ui-models/src/index.ts`](../packages/client/ui-models/src/index.ts))
|
- `@deepseek-ai/dsh-client-ui-models` ([`packages/client/ui-models/src/index.ts`](../packages/client/ui-models/src/index.ts))
|
||||||
- `@deepseek-ai/dsh-client-ui-permission` ([`packages/client/ui-permission/src/index.ts`](../packages/client/ui-permission/src/index.ts))
|
- `@deepseek-ai/dsh-client-ui-permission` ([`packages/client/ui-permission/src/index.ts`](../packages/client/ui-permission/src/index.ts))
|
||||||
- `@deepseek-ai/dsh-client-ui-plan` ([`packages/client/ui-plan/src/index.ts`](../packages/client/ui-plan/src/index.ts))
|
- `@deepseek-ai/dsh-client-ui-plan` ([`packages/client/ui-plan/src/index.ts`](../packages/client/ui-plan/src/index.ts))
|
||||||
- `@deepseek-ai/dsh-client-ui-question` — requires `tools` · `userInteraction` ([`packages/client/ui-question/src/index.ts`](../packages/client/ui-question/src/index.ts))
|
- `@deepseek-ai/dsh-client-ui-question` ([`packages/client/ui-question/src/index.ts`](../packages/client/ui-question/src/index.ts))
|
||||||
- `@deepseek-ai/dsh-client-ui-settings` ([`packages/client/ui-settings/src/index.ts`](../packages/client/ui-settings/src/index.ts))
|
- `@deepseek-ai/dsh-client-ui-settings` ([`packages/client/ui-settings/src/index.ts`](../packages/client/ui-settings/src/index.ts))
|
||||||
- `@deepseek-ai/dsh-client-ui-settings-general` ([`packages/client/ui-settings-general/src/index.ts`](../packages/client/ui-settings-general/src/index.ts))
|
- `@deepseek-ai/dsh-client-ui-settings-general` ([`packages/client/ui-settings-general/src/index.ts`](../packages/client/ui-settings-general/src/index.ts))
|
||||||
- `@deepseek-ai/dsh-client-ui-sidebar` ([`packages/client/ui-sidebar/src/index.ts`](../packages/client/ui-sidebar/src/index.ts))
|
- `@deepseek-ai/dsh-client-ui-sidebar` ([`packages/client/ui-sidebar/src/index.ts`](../packages/client/ui-sidebar/src/index.ts))
|
||||||
|
|||||||
@@ -204,8 +204,11 @@
|
|||||||
# absent from a surface overlay would silently reappear the day someone reorders
|
# absent from a surface overlay would silently reappear the day someone reorders
|
||||||
# the composition.
|
# the composition.
|
||||||
|
|
||||||
- id: bash-env
|
# `bash-env` STAYS in the host plane: `apps/cli/src/web.ts` injects it to
|
||||||
disabled: true
|
# publish `DSH_WEB_URL`/`DSH_WEB_MODE`, and a host row that injects a service is
|
||||||
|
# the criterion for host-plane ownership — injection resolves before any session
|
||||||
|
# exists, so there is no agent to key by. Behind a preset realm those variables
|
||||||
|
# would never reach the model's shell at all.
|
||||||
|
|
||||||
- id: tool-bash
|
- id: tool-bash
|
||||||
disabled: true
|
disabled: true
|
||||||
@@ -261,14 +264,12 @@
|
|||||||
- id: tool-result-prune
|
- id: tool-result-prune
|
||||||
disabled: true
|
disabled: true
|
||||||
|
|
||||||
- id: subagent
|
# The subagent registry and its backends STAY in the host plane. `subagents` is
|
||||||
disabled: true
|
# a process singleton with a cross-session query surface (`listChildren`,
|
||||||
|
# `followup`) that the host api-proxy serves to the browser, and a provider
|
||||||
- id: subagent-spawn
|
# registers under a globally unique name, so a per-session copy would both
|
||||||
disabled: true
|
# starve that host row and collide on the second session. What a preset
|
||||||
|
# chooses is which delegation TOOLS its agent sees, below.
|
||||||
- id: subagent-fork
|
|
||||||
disabled: true
|
|
||||||
|
|
||||||
- id: tool-subagent-control
|
- id: tool-subagent-control
|
||||||
disabled: true
|
disabled: true
|
||||||
@@ -282,8 +283,11 @@
|
|||||||
- id: tool-subagent-fork
|
- id: tool-subagent-fork
|
||||||
disabled: true
|
disabled: true
|
||||||
|
|
||||||
- id: tool-subagent-report
|
# `tool-subagent-report` is host-plane for the same reason as the registry, not
|
||||||
disabled: true
|
# because a preset may not want it: it registers a CONTINUABLE SETUP on that
|
||||||
|
# singleton rather than a tool this agent calls, and the setup list is not
|
||||||
|
# scope-aware — one copy per mounted preset means every child gets `report`
|
||||||
|
# registered once per live session, which throws on the second.
|
||||||
|
|
||||||
- id: workflow-workerthread
|
- id: workflow-workerthread
|
||||||
disabled: true
|
disabled: true
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
# 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:
|
# after editing either side, bring the other along and re-record with:
|
||||||
# pnpm run verify-translation-pairing --write packages/client/ui-question/README.md
|
# pnpm run verify-translation-pairing --write packages/client/ui-question/README.md
|
||||||
README.md: 72d94396771eec0a90b96008b1fd5e4a736a398c
|
README.md: d31ceb62c46cb7a720b52d9e2a6c92e98d1c7e42
|
||||||
README.zh.md: 6344327d268f1d0c2ec0aaaf29657ea040e51691
|
README.zh.md: 9f9ad01c3f1f661f60fe11ec072f487cb18c170a
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
English | [中文](README.zh.md)
|
English | [中文](README.zh.md)
|
||||||
|
|
||||||
Web `ask_user_question` feature plugin. Its host half mounts `dsh-tool-ask-user` only when the Web feature is selected; its browser half registers the `question` entry in the conversation-owned `conversation.composer` keyed slot.
|
Web question feature plugin: its browser half registers the `question` entry in the conversation-owned `conversation.composer` keyed slot. Its host half is empty on purpose — mounting `dsh-tool-ask-user` there put the tool in the registry's GLOBAL layer, which merges into every agent regardless of the preset that composed it, so a two-tool benchmark preset really presented three. Rendering a question is a host UI capability; having the tool is an agent capability, so the `tool-ask-user` row belongs to the presets that want it (and to the TUI composition, which has no presets).
|
||||||
|
|
||||||
The component renders one question at a time with progress navigation, single- and multi-select choices, recommendation badges derived from label suffixes, and custom answers. A multi-select draft keeps its selected labels while the user opens or edits the custom answer, so its submitted item may carry both `selected` and `custom`; a single-select custom answer remains exclusive. Question detail reuses the assistant-output `MarkdownText` primitive, including its GFM rendering and untrusted-content policy. The capped card keeps its title, navigation, and submission actions fixed while long detail and choices share an internal scroll region. Single-select choices advance immediately, and Enter submits once every question is answered or skipped; Enter during IME composition confirms the input candidate without advancing. It submits one structured answer batch for the whole request: “Skip this question” retains other drafts and emits the existing blank `{ selected: [] }` shape for that item, while close rejects the whole wait as `ASK_CANCELLED`.
|
The component renders one question at a time with progress navigation, single- and multi-select choices, recommendation badges derived from label suffixes, and custom answers. A multi-select draft keeps its selected labels while the user opens or edits the custom answer, so its submitted item may carry both `selected` and `custom`; a single-select custom answer remains exclusive. Question detail reuses the assistant-output `MarkdownText` primitive, including its GFM rendering and untrusted-content policy. The capped card keeps its title, navigation, and submission actions fixed while long detail and choices share an internal scroll region. Single-select choices advance immediately, and Enter submits once every question is answered or skipped; Enter during IME composition confirms the input candidate without advancing. It submits one structured answer batch for the whole request: “Skip this question” retains other drafts and emits the existing blank `{ selected: [] }` shape for that item, while close rejects the whole wait as `ASK_CANCELLED`.
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[English](README.md) | 中文
|
[English](README.md) | 中文
|
||||||
|
|
||||||
Web `ask_user_question` 功能插件。只有选择 Web 功能时,其主机侧才会挂载 `dsh-tool-ask-user`;浏览器侧会把 `question` 配置项注册到会话拥有的 `conversation.composer` 键控 slot 中。
|
Web 提问功能插件:其浏览器侧把 `question` 配置项注册到会话拥有的 `conversation.composer` 键控 slot 中。其主机侧刻意为空——在那里挂载 `dsh-tool-ask-user` 会把工具放进注册表的**全局层**,而全局层会并入每一个 agent,无论它由哪个 preset 组装,于是一个"两工具"的 benchmark preset 实际会呈现三个。渲染提问是宿主的 UI 能力,拥有该工具则是 agent 的能力,因此 `tool-ask-user` 行属于需要它的各个 preset(以及没有 preset 的 TUI 组装)。
|
||||||
|
|
||||||
组件每次渲染一个问题,提供进度导航、单选和多选选项、由标签后缀派生的推荐徽标,以及自定义答案。用户打开或编辑自定义答案时,多选题草稿会保留已选中的标签,因此提交项可以同时携带 `selected` 与 `custom`;单选题的自定义答案仍保持互斥。问题详情复用助手输出的 `MarkdownText` 原语,包括其 GFM 渲染与不受信任内容策略。限高卡片保持标题、导航与提交动作固定,超长的详情与选项共享内部滚动区。单选选项会立即前进;所有问题均已回答或跳过后,Enter 会提交;IME 输入法组合期间按 Enter 只会确认输入候选,不会前进。组件为整个请求提交一批结构化答案:「跳过此问题」会保留其他草稿,并为该项发出既有的空 `{ selected: [] }` 形状;关闭则以 `ASK_CANCELLED` 拒绝整个等待。
|
组件每次渲染一个问题,提供进度导航、单选和多选选项、由标签后缀派生的推荐徽标,以及自定义答案。用户打开或编辑自定义答案时,多选题草稿会保留已选中的标签,因此提交项可以同时携带 `selected` 与 `custom`;单选题的自定义答案仍保持互斥。问题详情复用助手输出的 `MarkdownText` 原语,包括其 GFM 渲染与不受信任内容策略。限高卡片保持标题、导航与提交动作固定,超长的详情与选项共享内部滚动区。单选选项会立即前进;所有问题均已回答或跳过后,Enter 会提交;IME 输入法组合期间按 Enter 只会确认输入候选,不会前进。组件为整个请求提交一批结构化答案:「跳过此问题」会保留其他草稿,并为该项发出既有的空 `{ selected: [] }` 形状;关闭则以 `ASK_CANCELLED` 拒绝整个等待。
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,6 @@
|
|||||||
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
|
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
|
||||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
||||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||||
"@deepseek-ai/dsh-tool-ask-user": "workspace:^",
|
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
"react": "^18.2.0"
|
"react": "^18.2.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
/**
|
/**
|
||||||
* Web question plugin, node half: enabling this UI feature also exposes the
|
* Web question plugin, node half.
|
||||||
* model-facing ask_user_question tool on the host composition.
|
*
|
||||||
|
* Deliberately empty. Mounting `ask_user_question` here put it in the tools
|
||||||
|
* registry's GLOBAL layer, so every agent saw it no matter which preset
|
||||||
|
* composed it — a two-tool benchmark preset actually presented three, and a
|
||||||
|
* locally authored `bash-only` preset presented two. Rendering a question is
|
||||||
|
* a host UI capability; having the tool is an agent capability, and only a
|
||||||
|
* preset decides that. The `tool-ask-user` row belongs in the presets that
|
||||||
|
* want it (and in the TUI composition, which has no presets).
|
||||||
*/
|
*/
|
||||||
import type { Context } from 'cordis'
|
|
||||||
import * as toolAskUser from '@deepseek-ai/dsh-tool-ask-user'
|
|
||||||
|
|
||||||
/** Host services required by the model-facing tool. */
|
/** Host plugin body — the model-facing tool is composed per preset, not here. */
|
||||||
export const inject = ['tools', 'userInteraction']
|
export function apply(): void {}
|
||||||
|
|
||||||
/**
|
|
||||||
* Mount ask_user_question for hosts that selected the Web question plugin.
|
|
||||||
* @param ctx - Host plugin context carrying tools and userInteraction.
|
|
||||||
*/
|
|
||||||
export function apply(ctx: Context): void {
|
|
||||||
toolAskUser.apply(ctx)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { afterEach, describe, expect, it } from 'vitest'
|
|||||||
import ToolRegistry from '@deepseek-ai/dsh-tools'
|
import ToolRegistry from '@deepseek-ai/dsh-tools'
|
||||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||||
import UserInteractionService from '@deepseek-ai/dsh-user-interaction'
|
import UserInteractionService from '@deepseek-ai/dsh-user-interaction'
|
||||||
import { apply, inject } from '../src/index.ts'
|
import { apply } from '../src/index.ts'
|
||||||
|
|
||||||
let ctx: Context | undefined
|
let ctx: Context | undefined
|
||||||
|
|
||||||
@@ -13,16 +13,19 @@ afterEach(async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('ui-question node plugin', () => {
|
describe('ui-question node plugin', () => {
|
||||||
it('exposes ask_user_question only for the selected Web feature lifecycle', async () => {
|
it('mounts no model-facing tool', async () => {
|
||||||
ctx = new Context()
|
ctx = new Context()
|
||||||
await ctx.plugin(SystemPrompt)
|
await ctx.plugin(SystemPrompt)
|
||||||
await ctx.plugin(ToolRegistry)
|
await ctx.plugin(ToolRegistry)
|
||||||
await ctx.plugin(UserInteractionService)
|
await ctx.plugin(UserInteractionService)
|
||||||
const feature = ctx.plugin({ inject: [...inject], apply })
|
|
||||||
await feature.await()
|
|
||||||
expect(ctx.tools.get('ask_user_question')).toBeDefined()
|
|
||||||
|
|
||||||
await feature.dispose()
|
await ctx.plugin({ apply }).await()
|
||||||
|
|
||||||
|
// Selecting the Web question FEATURE must not hand every agent the tool.
|
||||||
|
// `ctx.tools.register` on an unscoped host context files into the global
|
||||||
|
// layer, which merges into every agent's view regardless of the preset
|
||||||
|
// that composed it — so a two-tool benchmark preset would really present
|
||||||
|
// three. The `tool-ask-user` row belongs to the presets that want it.
|
||||||
expect(ctx.tools.get('ask_user_question')).toBeUndefined()
|
expect(ctx.tools.get('ask_user_question')).toBeUndefined()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -29,9 +29,7 @@
|
|||||||
{
|
{
|
||||||
"path": "../ui-slots"
|
"path": "../ui-slots"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "../../ui/tool-ask-user"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "../../support/invariants"
|
"path": "../../support/invariants"
|
||||||
}
|
}
|
||||||
|
|||||||
84
pnpm-lock.yaml
generated
84
pnpm-lock.yaml
generated
@@ -146,6 +146,24 @@ importers:
|
|||||||
'@deepseek-ai/dsh-base':
|
'@deepseek-ai/dsh-base':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../packages/bundle/base
|
version: link:../../packages/bundle/base
|
||||||
|
'@deepseek-ai/dsh-command-compact':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/compact/command-compact
|
||||||
|
'@deepseek-ai/dsh-command-goal':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/goal/command-goal
|
||||||
|
'@deepseek-ai/dsh-compact-basic':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/compact/compact-basic
|
||||||
|
'@deepseek-ai/dsh-compact-tool-result-prune':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/compact/compact-tool-result-prune
|
||||||
|
'@deepseek-ai/dsh-goal':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/goal/goal
|
||||||
|
'@deepseek-ai/dsh-goal-session':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/goal/goal-session
|
||||||
'@deepseek-ai/dsh-headless':
|
'@deepseek-ai/dsh-headless':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../packages/bundle/headless
|
version: link:../../packages/bundle/headless
|
||||||
@@ -155,6 +173,12 @@ importers:
|
|||||||
'@deepseek-ai/dsh-paths':
|
'@deepseek-ai/dsh-paths':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../packages/util/paths
|
version: link:../../packages/util/paths
|
||||||
|
'@deepseek-ai/dsh-persona':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/preset/persona
|
||||||
|
'@deepseek-ai/dsh-plan-mode':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/plan/plan-mode
|
||||||
'@deepseek-ai/dsh-pty':
|
'@deepseek-ai/dsh-pty':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../packages/pty/pty
|
version: link:../../packages/pty/pty
|
||||||
@@ -164,21 +188,78 @@ importers:
|
|||||||
'@deepseek-ai/dsh-session-reference':
|
'@deepseek-ai/dsh-session-reference':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../packages/context/session-reference
|
version: link:../../packages/context/session-reference
|
||||||
|
'@deepseek-ai/dsh-skill':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/skill/skill
|
||||||
|
'@deepseek-ai/dsh-skill-local':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/skill/skill-local
|
||||||
|
'@deepseek-ai/dsh-tasks-local':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/tasks/tasks-local
|
||||||
'@deepseek-ai/dsh-tmux-context':
|
'@deepseek-ai/dsh-tmux-context':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../packages/context/tmux-context
|
version: link:../../packages/context/tmux-context
|
||||||
|
'@deepseek-ai/dsh-token-meter':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/llm/token-meter
|
||||||
'@deepseek-ai/dsh-tool-ask-user':
|
'@deepseek-ai/dsh-tool-ask-user':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../packages/ui/tool-ask-user
|
version: link:../../packages/ui/tool-ask-user
|
||||||
|
'@deepseek-ai/dsh-tool-bash':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/bash/tool-bash
|
||||||
'@deepseek-ai/dsh-tool-bash-persistent':
|
'@deepseek-ai/dsh-tool-bash-persistent':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../packages/pty/tool-bash-persistent
|
version: link:../../packages/pty/tool-bash-persistent
|
||||||
'@deepseek-ai/dsh-tool-cordis':
|
'@deepseek-ai/dsh-tool-cordis':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../packages/cordis/tool-cordis
|
version: link:../../packages/cordis/tool-cordis
|
||||||
|
'@deepseek-ai/dsh-tool-fs':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/fs/tool-fs
|
||||||
|
'@deepseek-ai/dsh-tool-fs-search':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/fs/tool-fs-search
|
||||||
|
'@deepseek-ai/dsh-tool-goal':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/goal/tool-goal
|
||||||
|
'@deepseek-ai/dsh-tool-ralph':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/workflow/tool-ralph
|
||||||
|
'@deepseek-ai/dsh-tool-skill':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/skill/tool-skill
|
||||||
|
'@deepseek-ai/dsh-tool-str-replace-editor':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/fs/tool-str-replace-editor
|
||||||
|
'@deepseek-ai/dsh-tool-subagent':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/subagent/tool-subagent
|
||||||
|
'@deepseek-ai/dsh-tool-subagent-control':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/subagent/tool-subagent-control
|
||||||
|
'@deepseek-ai/dsh-tool-tasks':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/tasks/tool-tasks
|
||||||
|
'@deepseek-ai/dsh-tool-todo':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/todo/tool-todo
|
||||||
|
'@deepseek-ai/dsh-tool-web':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/web/tool-web
|
||||||
|
'@deepseek-ai/dsh-tool-workflow':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/workflow/tool-workflow
|
||||||
'@deepseek-ai/dsh-web-app':
|
'@deepseek-ai/dsh-web-app':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../../packages/bundle/web-app
|
version: link:../../packages/bundle/web-app
|
||||||
|
'@deepseek-ai/dsh-workflow-workerthread':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/workflow/workflow-workerthread
|
||||||
|
'@deepseek-ai/dsh-workspace-context':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../packages/context/workspace-context
|
||||||
commander:
|
commander:
|
||||||
specifier: ^15.0.0
|
specifier: ^15.0.0
|
||||||
version: 15.0.0
|
version: 15.0.0
|
||||||
@@ -1878,9 +1959,6 @@ importers:
|
|||||||
'@deepseek-ai/dsh-client-ui-slots':
|
'@deepseek-ai/dsh-client-ui-slots':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../ui-slots
|
version: link:../ui-slots
|
||||||
'@deepseek-ai/dsh-tool-ask-user':
|
|
||||||
specifier: workspace:^
|
|
||||||
version: link:../../ui/tool-ask-user
|
|
||||||
clsx:
|
clsx:
|
||||||
specifier: ^2.0.0
|
specifier: ^2.0.0
|
||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
|
|||||||
Reference in New Issue
Block a user