Review feedback (tianyicui): 'process' is a poor service name. The family is now packages/subprocess/ — @deepseek-ai/dsh-subprocess (ctx.subprocess, abstract SubprocessService, Subprocess* vocabulary) and @deepseek-ai/dsh-subprocess-local (LocalSubprocessService) — renamed throughout code, compositions, docs (en+zh, pairs re-recorded), catalogs, and gates. 'subprocess' is the precise term for managed OS children (the Python-stdlib sense), avoids colliding with Node's global process object, and reads as one system beside dsh-subagent-subprocess. ds-review-bot findings addressed: - kill() on a settled handle is now a no-op (no signal to a possibly-reused pgid, no referenced grace timer delaying exit); pinned by a spy test. - The moved DshEnvironmentKey/DshEnvironment/CollectedOutput types get drift-checked type-equiv blocks on the new subprocess.md page, restoring their manifest registration. - subprocess.md is registered in the core.md sub-page index (en+zh).
367 lines
12 KiB
TypeScript
367 lines
12 KiB
TypeScript
/**
|
|
* Ordered builtin feature catalog: behavior entities only where project
|
|
* context changes the contribution, typed specs everywhere else.
|
|
*
|
|
* @module @deepseek-ai/dsh-helper/features/builtin
|
|
*/
|
|
|
|
import type { Config as ClaudeHooksConfig } from '@deepseek-ai/dsh-hooks-claude'
|
|
import type { Config as CodexHooksConfig } from '@deepseek-ai/dsh-hooks-codex'
|
|
import type { Config as JsonlConfig } from '@deepseek-ai/dsh-session-persistence-jsonl'
|
|
import type { Config as SqliteConfig } from '@deepseek-ai/dsh-session-persistence-sqlite'
|
|
import type { Config as ToolSubagentConfig } from '@deepseek-ai/dsh-tool-subagent'
|
|
import type { Config as ToolWebConfig } from '@deepseek-ai/dsh-tool-web'
|
|
import type { ProjectProfile } from '../../project/types.ts'
|
|
import { defineFeatures } from '../define-feature.ts'
|
|
import { FeatureRegistry } from '../registry.ts'
|
|
import { AppFeature } from './app.ts'
|
|
import { ProviderFeature } from './provider.ts'
|
|
import { SpineFeature } from './spine.ts'
|
|
|
|
/**
|
|
* Build and definition-check the complete builtin set for one project profile.
|
|
* @param profile - project context used to validate conditional contributions.
|
|
* @returns ordered builtin feature registry.
|
|
*/
|
|
export function createBuiltinRegistry(profile: ProjectProfile): FeatureRegistry {
|
|
return new FeatureRegistry(defineFeatures([
|
|
new ProviderFeature(),
|
|
new SpineFeature(),
|
|
{
|
|
id: 'bash',
|
|
summary: 'Command execution',
|
|
mode: 'exclusive',
|
|
required: true,
|
|
baseResources: [
|
|
{ kind: 'npm-cordis-config-entry', id: 'subprocess', package: '@deepseek-ai/dsh-subprocess-local' },
|
|
{ kind: 'npm-cordis-config-entry', id: 'tool-bash', package: '@deepseek-ai/dsh-tool-bash' },
|
|
],
|
|
options: [
|
|
{
|
|
id: 'local',
|
|
label: 'Local executor',
|
|
default: true,
|
|
resources: [{ kind: 'npm-cordis-config-entry', id: 'bash', package: '@deepseek-ai/dsh-bash-local' }],
|
|
},
|
|
{
|
|
id: 'sandbox',
|
|
label: 'Sandboxed executor',
|
|
resources: [
|
|
{ kind: 'npm-cordis-config-entry', id: 'sandbox', package: '@deepseek-ai/dsh-sandbox-local' },
|
|
{
|
|
kind: 'npm-cordis-config-entry',
|
|
id: 'bash',
|
|
package: '@deepseek-ai/dsh-bash-sandbox',
|
|
commentedExample: `Uncomment to allow writes under the project workspace.
|
|
config:
|
|
mode: workspace-write
|
|
workspaceRoot: !!js process.cwd()`,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
new AppFeature(),
|
|
{
|
|
id: 'persistence',
|
|
summary: 'Durable session storage',
|
|
mode: 'exclusive',
|
|
required: true,
|
|
options: [
|
|
{
|
|
id: 'jsonl',
|
|
label: 'JSONL files',
|
|
default: true,
|
|
resources: [{
|
|
kind: 'npm-cordis-config-entry',
|
|
id: 'session-persistence',
|
|
package: '@deepseek-ai/dsh-session-persistence-jsonl',
|
|
config: { root: './.sessions' } satisfies JsonlConfig,
|
|
}],
|
|
},
|
|
{
|
|
id: 'sqlite',
|
|
label: 'SQLite database',
|
|
resources: [{
|
|
kind: 'npm-cordis-config-entry',
|
|
id: 'session-persistence',
|
|
package: '@deepseek-ai/dsh-session-persistence-sqlite',
|
|
config: { path: './.sessions/sessions.sqlite' } satisfies SqliteConfig,
|
|
}],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'hmr',
|
|
summary: 'Hot-module reload',
|
|
mode: 'single',
|
|
options: [{
|
|
id: 'default',
|
|
label: 'Cordis HMR',
|
|
default: true,
|
|
resources: [{ kind: 'npm-cordis-config-entry', id: 'hmr', package: '@cordisjs/plugin-hmr' }],
|
|
}],
|
|
},
|
|
{
|
|
id: 'fs',
|
|
summary: 'Read, write, and edit local files',
|
|
mode: 'single',
|
|
options: [{
|
|
id: 'local',
|
|
label: 'Local filesystem',
|
|
default: true,
|
|
resources: [
|
|
{ kind: 'npm-cordis-config-entry', id: 'fs-local', package: '@deepseek-ai/dsh-fs-local' },
|
|
{ kind: 'npm-cordis-config-entry', id: 'fs-policy', package: '@deepseek-ai/dsh-fs-policy' },
|
|
{ kind: 'npm-cordis-config-entry', id: 'tool-fs', package: '@deepseek-ai/dsh-tool-fs' },
|
|
],
|
|
}],
|
|
},
|
|
{
|
|
id: 'todo',
|
|
summary: 'Model-facing task tracking',
|
|
mode: 'single',
|
|
options: [{
|
|
id: 'default',
|
|
label: 'todo_write tool',
|
|
default: true,
|
|
resources: [{ kind: 'npm-cordis-config-entry', id: 'tool-todo', package: '@deepseek-ai/dsh-tool-todo' }],
|
|
}],
|
|
},
|
|
{
|
|
id: 'skill',
|
|
summary: 'Local skill discovery',
|
|
mode: 'single',
|
|
options: [{
|
|
id: 'default',
|
|
label: 'Local skills and skill tool',
|
|
default: true,
|
|
resources: [
|
|
{ kind: 'npm-cordis-config-entry', id: 'skill', package: '@deepseek-ai/dsh-skill' },
|
|
{ kind: 'npm-cordis-config-entry', id: 'skill-local', package: '@deepseek-ai/dsh-skill-local' },
|
|
{ kind: 'npm-cordis-config-entry', id: 'tool-skill', package: '@deepseek-ai/dsh-tool-skill' },
|
|
],
|
|
}],
|
|
},
|
|
{
|
|
id: 'web',
|
|
summary: 'Web search and fetch tools',
|
|
mode: 'exclusive',
|
|
suggests: ['timeout-policy'],
|
|
baseResources: [
|
|
{ kind: 'npm-cordis-config-entry', id: 'web', package: '@deepseek-ai/dsh-web' },
|
|
{ kind: 'npm-cordis-config-entry', id: 'web-fetch-local', package: '@deepseek-ai/dsh-web-fetch-local' },
|
|
],
|
|
options: [
|
|
{
|
|
id: 'deepseek',
|
|
label: 'DeepSeek search',
|
|
default: true,
|
|
markers: [{ id: 'web-search-deepseek', name: '@deepseek-ai/dsh-web-search-deepseek' }],
|
|
resources: [
|
|
{ kind: 'npm-cordis-config-entry', id: 'web-search-deepseek', package: '@deepseek-ai/dsh-web-search-deepseek' },
|
|
{ kind: 'npm-cordis-config-entry', id: 'tool-web', package: '@deepseek-ai/dsh-tool-web' },
|
|
],
|
|
},
|
|
{
|
|
id: 'exa',
|
|
label: 'Exa search',
|
|
secrets: [{ id: 'apiKey', environment: 'EXA_API_KEY', message: 'Exa API key', required: true }],
|
|
markers: [{ id: 'web-search-exa', name: '@deepseek-ai/dsh-web-search-exa' }],
|
|
resources: [
|
|
{ kind: 'npm-cordis-config-entry', id: 'web-search-exa', package: '@deepseek-ai/dsh-web-search-exa' },
|
|
{ kind: 'npm-cordis-config-entry', id: 'tool-web', package: '@deepseek-ai/dsh-tool-web' },
|
|
],
|
|
},
|
|
{
|
|
id: 'perplexity',
|
|
label: 'Perplexity search',
|
|
secrets: [{
|
|
id: 'apiKey',
|
|
environment: 'PERPLEXITY_API_KEY',
|
|
message: 'Perplexity API key',
|
|
required: true,
|
|
}],
|
|
markers: [{ id: 'web-search-perplexity', name: '@deepseek-ai/dsh-web-search-perplexity' }],
|
|
resources: [
|
|
{
|
|
kind: 'npm-cordis-config-entry',
|
|
id: 'web-search-perplexity',
|
|
package: '@deepseek-ai/dsh-web-search-perplexity',
|
|
},
|
|
{ kind: 'npm-cordis-config-entry', id: 'tool-web', package: '@deepseek-ai/dsh-tool-web' },
|
|
],
|
|
},
|
|
{
|
|
id: 'fetch-only',
|
|
label: 'Fetch only',
|
|
markers: [{ id: 'tool-web', name: '@deepseek-ai/dsh-tool-web', config: { search: false } }],
|
|
resources: [{
|
|
kind: 'npm-cordis-config-entry',
|
|
id: 'tool-web',
|
|
package: '@deepseek-ai/dsh-tool-web',
|
|
config: { search: false } satisfies ToolWebConfig,
|
|
}],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'subagent',
|
|
summary: 'Delegate work to child agents',
|
|
mode: 'multiple',
|
|
baseResources: [{ kind: 'npm-cordis-config-entry', id: 'subagent', package: '@deepseek-ai/dsh-subagent' }],
|
|
options: [
|
|
{
|
|
id: 'spawn',
|
|
label: 'Fresh child agent',
|
|
default: true,
|
|
resources: [
|
|
{ kind: 'npm-cordis-config-entry', id: 'subagent-spawn', package: '@deepseek-ai/dsh-subagent-spawn' },
|
|
{
|
|
kind: 'npm-cordis-config-entry',
|
|
id: 'tool-subagent',
|
|
package: '@deepseek-ai/dsh-tool-subagent',
|
|
config: { provider: 'spawn' } satisfies ToolSubagentConfig,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'fork',
|
|
label: 'Fork parent history',
|
|
resources: [
|
|
{ kind: 'npm-cordis-config-entry', id: 'subagent-fork', package: '@deepseek-ai/dsh-subagent-fork' },
|
|
{
|
|
kind: 'npm-cordis-config-entry',
|
|
id: 'tool-subagent-fork',
|
|
package: '@deepseek-ai/dsh-tool-subagent',
|
|
config: { provider: 'fork', toolName: 'subagent_fork' } satisfies ToolSubagentConfig,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'workflow',
|
|
summary: 'Scripted multi-agent workflows',
|
|
mode: 'single',
|
|
options: [{
|
|
id: 'workerthread',
|
|
label: 'Worker thread engine',
|
|
default: true,
|
|
requires: [{ id: 'subagent', options: ['spawn'] }],
|
|
resources: [
|
|
{
|
|
kind: 'npm-cordis-config-entry',
|
|
id: 'workflow-workerthread',
|
|
package: '@deepseek-ai/dsh-workflow-workerthread',
|
|
},
|
|
{ kind: 'npm-cordis-config-entry', id: 'tool-workflow', package: '@deepseek-ai/dsh-tool-workflow' },
|
|
],
|
|
}],
|
|
},
|
|
{
|
|
id: 'compact',
|
|
summary: 'Automatic context compaction',
|
|
mode: 'single',
|
|
options: [{
|
|
id: 'basic',
|
|
label: 'Basic compaction',
|
|
default: true,
|
|
resources: [
|
|
{
|
|
kind: 'npm-cordis-config-entry',
|
|
id: 'token-meter',
|
|
package: '@deepseek-ai/dsh-token-meter',
|
|
},
|
|
{
|
|
kind: 'npm-cordis-config-entry',
|
|
id: 'compact-basic',
|
|
package: '@deepseek-ai/dsh-compact-basic',
|
|
},
|
|
],
|
|
}],
|
|
},
|
|
{
|
|
id: 'hooks',
|
|
summary: 'Run Claude Code or Codex hooks',
|
|
mode: 'multiple',
|
|
requires: [{ id: 'bash' }],
|
|
options: [
|
|
{
|
|
id: 'claude',
|
|
label: 'Claude Code hooks',
|
|
default: true,
|
|
resources: [
|
|
{
|
|
kind: 'npm-cordis-config-entry',
|
|
id: 'hooks-claude',
|
|
package: '@deepseek-ai/dsh-hooks-claude',
|
|
config: { configPath: './hooks.json' } satisfies ClaudeHooksConfig,
|
|
},
|
|
{ kind: 'owned-file', path: 'hooks.json', text: '{}' },
|
|
],
|
|
},
|
|
{
|
|
id: 'codex',
|
|
label: 'Codex hooks',
|
|
resources: [
|
|
{
|
|
kind: 'npm-cordis-config-entry',
|
|
id: 'hooks-codex',
|
|
package: '@deepseek-ai/dsh-hooks-codex',
|
|
config: { configPath: './codex-hooks.json' } satisfies CodexHooksConfig,
|
|
},
|
|
{ kind: 'owned-file', path: 'codex-hooks.json', text: '{}' },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'guard',
|
|
summary: 'Loop-hygiene reminders',
|
|
mode: 'single',
|
|
options: [{
|
|
id: 'repeat-tool',
|
|
label: 'Repeat-tool reminders',
|
|
default: true,
|
|
resources: [{
|
|
kind: 'npm-cordis-config-entry',
|
|
id: 'repeat-tool-guard',
|
|
package: '@deepseek-ai/dsh-repeat-tool-guard',
|
|
}],
|
|
}],
|
|
},
|
|
{
|
|
id: 'timeout-policy',
|
|
summary: 'Tool timeout policy',
|
|
mode: 'single',
|
|
options: [{
|
|
id: 'default',
|
|
label: 'Timeout policy',
|
|
default: true,
|
|
resources: [{
|
|
kind: 'npm-cordis-config-entry',
|
|
id: 'timeout-policy',
|
|
package: '@deepseek-ai/dsh-timeout-policy',
|
|
}],
|
|
}],
|
|
},
|
|
{
|
|
id: 'ask-user',
|
|
summary: 'Ask the user from the model loop',
|
|
mode: 'single',
|
|
supportedInterfaces: ['tui'],
|
|
options: [{
|
|
id: 'default',
|
|
label: 'ask_user_question tool',
|
|
default: true,
|
|
resources: [{
|
|
kind: 'npm-cordis-config-entry',
|
|
id: 'tool-ask-user',
|
|
package: '@deepseek-ai/dsh-tool-ask-user',
|
|
}],
|
|
}],
|
|
},
|
|
]), profile)
|
|
}
|