feat: add economy/maximum presets, tool-lab and subagent-cursor extensions
Some checks failed
CI / windows node 24 / wine blocking (push) Has been skipped
CI / node 22.19 (push) Has been skipped
CI / node 26 (push) Has been skipped
CI / python 3.10 / keyless SDK (push) Has been skipped
CI / python runtime / release-shaped Linux x64 (push) Has been skipped
CI / wine apt cache (push) Successful in 7s
CI / serial / linux (push) Has been skipped
Deploy documentation / build (push) Failing after 1m25s
Deploy documentation / deploy (push) Has been skipped
Landlock Run / Matrix (push) Successful in 5s
Release (vendor) / Pack npm tarballs (push) Failing after 2m47s
Release (dsh) / Pack npm tarballs (push) Failing after 1m56s
Sandbox / sandbox e2e (landlock, ubuntu-24.04) (push) Failing after 1m57s
Sandbox / sandbox e2e (bwrap, ubuntu-latest) (push) Failing after 1m19s
Release (vendor) / Publish to npm (push) Has been skipped
Release (dsh) / Publish to npm (push) Has been skipped
CI / serial / windows (self-hosted standby) (push) Has been cancelled
CI / larger-runner-benchmark (16, linux, dsh-ubuntu-24-04-16core, typecheck) (push) Has been cancelled
Landlock Run / darwin (no platform package — degradation proof) (push) Has been cancelled
Landlock Run / ${{ matrix.platform }} (push) Has been cancelled
CI / node 24 / static (push) Has been cancelled
CI / node 24 / coverage (push) Has been cancelled
CI / node 24 / snapshots and artifacts (push) Has been cancelled
CI / windows node 24 / native complete (push) Has been cancelled
CI / serial / linux (self-hosted standby) (push) Has been cancelled
CI / serial / macos (push) Has been cancelled
CI / larger-runner-benchmark (16, windows, dsh-windows-2025-16core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (32, linux, dsh-ubuntu-24-04-32core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (32, windows, dsh-windows-2025-32core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (4, linux, dsh-ubuntu-24-04-4core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (4, windows, dsh-windows-2025-4core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (64, linux, dsh-ubuntu-24-04-64core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (64, windows, dsh-windows-2025-64core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (8, windows, dsh-windows-2025-8core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (96, linux, dsh-ubuntu-24-04-96core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (96, windows, dsh-windows-2025-96core, production-site) (push) Has been cancelled
CI / consolidated-runner-benchmark (16, linux, dsh-ubuntu-24-04-16core, 16) (push) Has been cancelled
CI / consolidated-runner-benchmark (16, windows, dsh-windows-2025-16core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (32, linux, dsh-ubuntu-24-04-32core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (32, windows, dsh-windows-2025-32core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (4, linux, dsh-ubuntu-24-04-4core, 4) (push) Has been cancelled
CI / consolidated-runner-benchmark (4, windows, dsh-windows-2025-4core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (64, linux, dsh-ubuntu-24-04-64core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (64, windows, dsh-windows-2025-64core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (8, linux, dsh-ubuntu-24-04-8core, 8) (push) Has been cancelled
CI / consolidated-runner-benchmark (8, windows, dsh-windows-2025-8core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (96, linux, dsh-ubuntu-24-04-96core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (96, windows, dsh-windows-2025-96core, 2) (push) Has been cancelled
CI / all checks passed (push) Has been cancelled
Sandbox / sandbox e2e (seatbelt, macos-latest) (push) Has been cancelled
CI / larger-runner-benchmark (8, linux, dsh-ubuntu-24-04-8core, typecheck) (push) Has been cancelled
Sandbox / sandbox e2e (landlock, ubuntu-24.04-arm) (push) Has been cancelled
E2E (real DeepSeek API) / e2e (push) Failing after 1m24s
Some checks failed
CI / windows node 24 / wine blocking (push) Has been skipped
CI / node 22.19 (push) Has been skipped
CI / node 26 (push) Has been skipped
CI / python 3.10 / keyless SDK (push) Has been skipped
CI / python runtime / release-shaped Linux x64 (push) Has been skipped
CI / wine apt cache (push) Successful in 7s
CI / serial / linux (push) Has been skipped
Deploy documentation / build (push) Failing after 1m25s
Deploy documentation / deploy (push) Has been skipped
Landlock Run / Matrix (push) Successful in 5s
Release (vendor) / Pack npm tarballs (push) Failing after 2m47s
Release (dsh) / Pack npm tarballs (push) Failing after 1m56s
Sandbox / sandbox e2e (landlock, ubuntu-24.04) (push) Failing after 1m57s
Sandbox / sandbox e2e (bwrap, ubuntu-latest) (push) Failing after 1m19s
Release (vendor) / Publish to npm (push) Has been skipped
Release (dsh) / Publish to npm (push) Has been skipped
CI / serial / windows (self-hosted standby) (push) Has been cancelled
CI / larger-runner-benchmark (16, linux, dsh-ubuntu-24-04-16core, typecheck) (push) Has been cancelled
Landlock Run / darwin (no platform package — degradation proof) (push) Has been cancelled
Landlock Run / ${{ matrix.platform }} (push) Has been cancelled
CI / node 24 / static (push) Has been cancelled
CI / node 24 / coverage (push) Has been cancelled
CI / node 24 / snapshots and artifacts (push) Has been cancelled
CI / windows node 24 / native complete (push) Has been cancelled
CI / serial / linux (self-hosted standby) (push) Has been cancelled
CI / serial / macos (push) Has been cancelled
CI / larger-runner-benchmark (16, windows, dsh-windows-2025-16core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (32, linux, dsh-ubuntu-24-04-32core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (32, windows, dsh-windows-2025-32core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (4, linux, dsh-ubuntu-24-04-4core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (4, windows, dsh-windows-2025-4core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (64, linux, dsh-ubuntu-24-04-64core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (64, windows, dsh-windows-2025-64core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (8, windows, dsh-windows-2025-8core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (96, linux, dsh-ubuntu-24-04-96core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (96, windows, dsh-windows-2025-96core, production-site) (push) Has been cancelled
CI / consolidated-runner-benchmark (16, linux, dsh-ubuntu-24-04-16core, 16) (push) Has been cancelled
CI / consolidated-runner-benchmark (16, windows, dsh-windows-2025-16core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (32, linux, dsh-ubuntu-24-04-32core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (32, windows, dsh-windows-2025-32core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (4, linux, dsh-ubuntu-24-04-4core, 4) (push) Has been cancelled
CI / consolidated-runner-benchmark (4, windows, dsh-windows-2025-4core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (64, linux, dsh-ubuntu-24-04-64core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (64, windows, dsh-windows-2025-64core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (8, linux, dsh-ubuntu-24-04-8core, 8) (push) Has been cancelled
CI / consolidated-runner-benchmark (8, windows, dsh-windows-2025-8core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (96, linux, dsh-ubuntu-24-04-96core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (96, windows, dsh-windows-2025-96core, 2) (push) Has been cancelled
CI / all checks passed (push) Has been cancelled
Sandbox / sandbox e2e (seatbelt, macos-latest) (push) Has been cancelled
CI / larger-runner-benchmark (8, linux, dsh-ubuntu-24-04-8core, typecheck) (push) Has been cancelled
Sandbox / sandbox e2e (landlock, ubuntu-24.04-arm) (push) Has been cancelled
E2E (real DeepSeek API) / e2e (push) Failing after 1m24s
- new economy and maximum agent presets with three-role pipeline skill - new packages/extensions/tool-lab (home-lab ComfyUI/Docling/Whishper tools) - new packages/subagent/subagent-cursor provider - openrouter balance UI with on-demand refresh - session projection context-seed boundary fold - regenerate docs catalogs; keep local searxng benchmark scripts
This commit is contained in:
@@ -150,6 +150,21 @@ function enablePresetTool(composition: string, id: string): string {
|
||||
return composition.slice(0, disabled) + composition.slice(disabled + ' disabled: true\n'.length)
|
||||
}
|
||||
|
||||
function disablePresetTool(composition: string, id: string): string {
|
||||
const row = ` - id: ${id}\n`
|
||||
const start = composition.indexOf(row)
|
||||
if (start < 0) throw new Error(`missing preset row ${id}`)
|
||||
const end = composition.indexOf('\n - id:', start + row.length)
|
||||
const disabled = composition.indexOf(' disabled: true\n', start)
|
||||
if (disabled >= 0 && (end < 0 || disabled < end)) {
|
||||
throw new Error(`preset row ${id} is already disabled`)
|
||||
}
|
||||
// `disabled` is entry metadata, so it goes beside `name` rather than inside
|
||||
// the row's `config`.
|
||||
const nameLine = composition.indexOf('\n', start + row.length) + 1
|
||||
return `${composition.slice(0, nameLine)} disabled: true\n${composition.slice(nameLine)}`
|
||||
}
|
||||
|
||||
let ctx: Context
|
||||
beforeAll(async () => {
|
||||
const settingsFile = join(await mkdtemp(join(tmpdir(), 'dsh-web-presets-')), 'settings.yaml')
|
||||
@@ -197,7 +212,7 @@ describe('the shipped Web composition', () => {
|
||||
it('supplies both shipped presets, and only those, from the system root', async () => {
|
||||
const listed = await ctx.agentPresets.list()
|
||||
|
||||
expect(listed.map(preset => preset.id).sort()).toEqual(['code', 'cordis', 'minimal', 'standard'])
|
||||
expect(listed.map(preset => preset.id).sort()).toEqual(['code', 'cordis', 'economy', 'maximum', 'minimal', 'standard'])
|
||||
expect(listed.every(preset => preset.trust === 'system')).toBe(true)
|
||||
expect(ctx.agentPresets.defaultId).toBe('standard')
|
||||
})
|
||||
@@ -216,7 +231,8 @@ describe('the shipped Web composition', () => {
|
||||
expect(toolNames(ctx, handle.agent).filter(name => name !== 'glob' && name !== 'grep')).toEqual([
|
||||
'ask_user_question', 'bash', 'create_goal', 'edit', 'exit_plan_mode',
|
||||
'get_goal', 'interrupt_agent', 'job_kill', 'job_list', 'job_output', 'list_agents', 'ralph', 'read', 'read_image', 'send_message', 'skill',
|
||||
'subagent', 'subagent_fork', 'todo_write', 'update_goal', 'web_search',
|
||||
'subagent', 'subagent_claude_code', 'subagent_codex', 'subagent_cursor',
|
||||
'subagent_fork', 'todo_write', 'update_goal', 'web_search',
|
||||
'workflow', 'write',
|
||||
])
|
||||
} finally {
|
||||
@@ -224,6 +240,75 @@ describe('the shipped Web composition', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('composes the economy agent without external subagent providers', async () => {
|
||||
const handle = await ctx.agents.create({
|
||||
sessionId: SessionId('preset-economy'),
|
||||
setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'economy').then(() => undefined),
|
||||
})
|
||||
try {
|
||||
const toolList = toolNames(ctx, handle.agent).filter(name => name !== 'glob' && name !== 'grep')
|
||||
// The shell row is platform-gated like `standard`'s: bash off Windows,
|
||||
// pwsh on it. Everything else is the standard catalog.
|
||||
const shell = process.platform === 'win32' ? 'pwsh' : 'bash'
|
||||
// `economy` keeps every standard tool except the external product
|
||||
// agents, which it disables to avoid paid delegations by default.
|
||||
expect(toolList).toEqual(expect.arrayContaining([
|
||||
'ask_user_question', shell, 'create_goal', 'edit', 'exit_plan_mode',
|
||||
'get_goal', 'interrupt_agent', 'job_kill', 'job_list', 'job_output', 'list_agents', 'ralph', 'read', 'read_image', 'send_message', 'skill',
|
||||
'subagent', 'subagent_fork', 'todo_write', 'update_goal', 'web_search',
|
||||
'workflow', 'write',
|
||||
]))
|
||||
expect(toolList).not.toEqual(expect.arrayContaining([
|
||||
'subagent_codex', 'subagent_claude_code', 'subagent_cursor',
|
||||
]))
|
||||
// The delegation tools default to foreground: one-shot, not the
|
||||
// continuable background scheduling `standard` uses.
|
||||
const subagent = ctx.tools.schemas(handle.agent).find(schema => schema.name === 'subagent')
|
||||
expect(subagent?.description).toContain('This call waits for the result by default.')
|
||||
} finally {
|
||||
await handle.dispose()
|
||||
}
|
||||
})
|
||||
|
||||
it('composes every shipped capability and the three pipeline roles from `maximum`', async () => {
|
||||
const handle = await ctx.agents.create({
|
||||
sessionId: SessionId('preset-maximum'),
|
||||
setup: agentCtx => ctx.agentPresets.mount(agentCtx, 'maximum').then(() => undefined),
|
||||
})
|
||||
try {
|
||||
// The shell row is platform-gated like `standard`'s: bash off Windows,
|
||||
// pwsh on it. The registry answers in name order, so the gated name is
|
||||
// sorted in rather than written at a fixed position.
|
||||
const shell = process.platform === 'win32' ? 'pwsh' : 'bash'
|
||||
// The EXACT catalog, for the reason `standard`'s assertion states: a row
|
||||
// that registers into the wrong layer mounts cleanly and contributes
|
||||
// nothing, and this preset exists to carry every row at once.
|
||||
expect(toolNames(ctx, handle.agent).filter(name => name !== 'glob' && name !== 'grep')).toEqual([
|
||||
'ask_user_question', shell, 'cordis_define', 'cordis_inspect_list', 'cordis_inspect_query',
|
||||
'cordis_inspect_self', 'cordis_run', 'cordis_stop', 'cordis_undefine', 'create_goal', 'edit',
|
||||
'exit_plan_mode', 'get_goal', 'interrupt_agent', 'job_kill', 'job_list', 'job_output',
|
||||
'list_agents', 'lsp', 'ralph', 'read', 'read_image', 'run_code', 'schedule_create',
|
||||
'schedule_delete', 'schedule_list', 'send_message', 'session_event_read',
|
||||
'session_event_search', 'session_event_trace', 'session_search', 'session_trace', 'skill',
|
||||
'str_replace_editor', 'subagent', 'subagent_architect', 'subagent_claude_code',
|
||||
'subagent_codex', 'subagent_cursor', 'subagent_fork', 'subagent_implementer',
|
||||
'subagent_reviewer', 'terminal_close', 'terminal_list', 'terminal_open', 'terminal_read',
|
||||
'terminal_send', 'terminal_signal', 'todo_write', 'update_goal', 'web_search', 'workflow',
|
||||
'write',
|
||||
].sort())
|
||||
// Each role is one `spawn` instance carrying its own child persona, so
|
||||
// the roles are distinguishable only by what their descriptions and
|
||||
// personas say — the wire schemas are otherwise identical.
|
||||
const schemas = ctx.tools.schemas(handle.agent)
|
||||
for (const role of ['subagent_architect', 'subagent_implementer', 'subagent_reviewer']) {
|
||||
expect(schemas.find(schema => schema.name === role)?.description)
|
||||
.toContain('This call waits for the result by default.')
|
||||
}
|
||||
} finally {
|
||||
await handle.dispose()
|
||||
}
|
||||
})
|
||||
|
||||
it('composes the exact RL prompt and two tools from `minimal`', async () => {
|
||||
const handle = await ctx.agents.create({
|
||||
sessionId: SessionId('preset-minimal'),
|
||||
@@ -436,7 +521,7 @@ describe('the shipped Web composition', () => {
|
||||
|
||||
describe('product subagent rows in user presets', () => {
|
||||
let productCtx: Context
|
||||
const ids = ['products-none', 'products-codex', 'products-claude', 'products-both'] as const
|
||||
const ids = ['products-none', 'products-codex', 'products-claude', 'products-all'] as const
|
||||
|
||||
beforeAll(async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), 'dsh-product-presets-'))
|
||||
@@ -444,23 +529,26 @@ describe('product subagent rows in user presets', () => {
|
||||
const settingsFile = join(root, 'settings.yaml')
|
||||
const standard = await readFile(join(CONFIG_DIR, 'agent-presets', 'standard', 'agent.cordis.yml'), 'utf8')
|
||||
await writeFile(settingsFile, '{}\n')
|
||||
// `standard` ships every product row enabled, so each variant is built by
|
||||
// REMOVING the rows it must not carry.
|
||||
for (const id of ids) {
|
||||
let composition = standard
|
||||
if (id === 'products-codex' || id === 'products-both') {
|
||||
composition = enablePresetTool(composition, 'tool-subagent-codex')
|
||||
if (id !== 'products-all' && id !== 'products-codex') {
|
||||
composition = disablePresetTool(composition, 'tool-subagent-codex')
|
||||
}
|
||||
if (id === 'products-claude' || id === 'products-both') {
|
||||
composition = enablePresetTool(composition, 'tool-subagent-claude-code')
|
||||
if (id !== 'products-all' && id !== 'products-claude') {
|
||||
composition = disablePresetTool(composition, 'tool-subagent-claude-code')
|
||||
}
|
||||
if (id !== 'products-all') {
|
||||
composition = disablePresetTool(composition, 'tool-subagent-cursor')
|
||||
}
|
||||
const directory = join(userRoot, id)
|
||||
await mkdir(directory, { recursive: true })
|
||||
await writeFile(join(directory, 'agent.cordis.yml'), composition)
|
||||
}
|
||||
// The base patch already mounts every product provider on the host plane,
|
||||
// so this boot adds only the preset roster.
|
||||
productCtx = await bootWeb(settingsFile, [
|
||||
{ insert: [
|
||||
{ id: 'subagent-codex', name: '@deepseek-ai/dsh-subagent-codex' },
|
||||
{ id: 'subagent-claude-code', name: '@deepseek-ai/dsh-subagent-claude-code' },
|
||||
] },
|
||||
{
|
||||
id: 'agent-presets',
|
||||
config: {
|
||||
@@ -479,15 +567,15 @@ describe('product subagent rows in user presets', () => {
|
||||
await productCtx.fiber.dispose()
|
||||
})
|
||||
|
||||
it('composes none, either product, or both without changing the shared host registry', async () => {
|
||||
it('composes none, one product, or every product without changing the shared host registry', async () => {
|
||||
const expected = new Map<string, string[]>([
|
||||
['products-none', []],
|
||||
['products-codex', ['subagent_codex']],
|
||||
['products-claude', ['subagent_claude_code']],
|
||||
['products-both', ['subagent_claude_code', 'subagent_codex']],
|
||||
['products-all', ['subagent_claude_code', 'subagent_codex', 'subagent_cursor']],
|
||||
])
|
||||
expect(productCtx.subagents.list()).toEqual(expect.arrayContaining([
|
||||
'spawn', 'fork', 'codex', 'claude-code',
|
||||
'spawn', 'fork', 'codex', 'claude-code', 'cursor',
|
||||
]))
|
||||
|
||||
for (const [id, productTools] of expected) {
|
||||
@@ -497,7 +585,7 @@ describe('product subagent rows in user presets', () => {
|
||||
})
|
||||
try {
|
||||
const tools = toolNames(productCtx, handle.agent)
|
||||
expect(tools.filter(name => name === 'subagent_codex' || name === 'subagent_claude_code'))
|
||||
expect(tools.filter(name => name.startsWith('subagent_') && name !== 'subagent_fork'))
|
||||
.toEqual(productTools)
|
||||
expect(tools).toEqual(expect.arrayContaining(['job_kill', 'job_list', 'job_output']))
|
||||
for (const productTool of productTools) {
|
||||
|
||||
Reference in New Issue
Block a user