refactor(acp): reduce bridge to automation protocol
This commit is contained in:
@@ -168,7 +168,7 @@ const PROJECT_QUESTION_STEPS: readonly WizardStep<ProjectAnswerState>[] = [
|
||||
id: 'interface',
|
||||
message: 'Run interface',
|
||||
options: [
|
||||
{ value: 'acp', label: 'ACP server' },
|
||||
{ value: 'acp', label: 'ACP automation server' },
|
||||
{ value: 'tui', label: 'Terminal TUI' },
|
||||
{ value: 'embed', label: 'Embedded context' },
|
||||
],
|
||||
|
||||
@@ -183,7 +183,7 @@ describe.skipIf(process.platform === 'win32')('create-sdk terminal contract', ()
|
||||
"kind": "select",
|
||||
"message": "Run interface",
|
||||
"options": [
|
||||
"ACP server",
|
||||
"ACP automation server",
|
||||
"Terminal TUI",
|
||||
"Embedded context",
|
||||
],
|
||||
@@ -287,12 +287,6 @@ describe.skipIf(process.platform === 'win32')('create-sdk terminal contract', ()
|
||||
"label": "Tool timeout policy",
|
||||
"required": false,
|
||||
},
|
||||
{
|
||||
"choices": undefined,
|
||||
"default": false,
|
||||
"label": "Ask the user from the model loop",
|
||||
"required": false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ The package owns the builtin typed-spec catalog, provider/app behavior entities,
|
||||
|
||||
All business and document validation completes before commit writes any affected file. Commit detects external edits made after the session opened, but deliberately provides no cross-file rollback after writing starts.
|
||||
|
||||
Builtin features are provider, bash, app, persistence, HMR, filesystem, todo, skill, web, subagent, workflow, compaction, hooks, repeat-tool guard, timeout policy, and ask-user. The catalog owns feature options, required and non-default Cordis plugin config, feature requirements, resource contribution, and round-trip markers; create and config use the same registry and configurator. The ACP app option contributes the human-command and user-interaction services before the bridge.
|
||||
Builtin features are provider, bash, app, persistence, HMR, filesystem, todo, skill, web, subagent, workflow, compaction, hooks, repeat-tool guard, timeout policy, and ask-user. The catalog owns feature options, required and non-default Cordis plugin config, feature requirements, resource contribution, and round-trip markers; create and config use the same registry and configurator. The ACP app option contributes only the automation bridge; interactive services belong to TUI or Web compositions.
|
||||
|
||||
`SdkProject.open()` requires only readable root `package.json` and `cordis.yml`. A Cordis config entry anchors feature installation; a package present only through a linked NPM dependency closure leaves the feature absent. Once an owned Cordis config entry exists, an incomplete resource shape is `inconsistent` and cannot be modified automatically.
|
||||
|
||||
|
||||
@@ -73,14 +73,6 @@ class AppOption extends FeatureOption {
|
||||
case 'acp':
|
||||
return new ProjectContribution([
|
||||
...appProjectResources(profile, this.id),
|
||||
...npmCordisConfigEntry(ID, {
|
||||
id: 'commands',
|
||||
name: '@deepseek-ai/dsh-commands',
|
||||
}),
|
||||
...npmCordisConfigEntry(ID, {
|
||||
id: 'user-interaction',
|
||||
name: '@deepseek-ai/dsh-user-interaction',
|
||||
}),
|
||||
...npmCordisConfigEntry(ID, {
|
||||
id: 'acp',
|
||||
name: '@deepseek-ai/dsh-acp',
|
||||
@@ -119,7 +111,7 @@ export class AppFeature extends ExclusiveOptionFeature {
|
||||
override readonly required = true
|
||||
override readonly requires = [featureId('spine')]
|
||||
override readonly options = [
|
||||
new AppOption('acp', 'ACP server'),
|
||||
new AppOption('acp', 'ACP automation server'),
|
||||
new AppOption('tui', 'Terminal TUI'),
|
||||
new AppOption('embed', 'Embedded context'),
|
||||
]
|
||||
|
||||
@@ -347,7 +347,7 @@ config:
|
||||
id: 'ask-user',
|
||||
summary: 'Ask the user from the model loop',
|
||||
mode: 'single',
|
||||
supportedInterfaces: ['acp', 'tui'],
|
||||
supportedInterfaces: ['tui'],
|
||||
options: [{
|
||||
id: 'default',
|
||||
label: 'ask_user_question tool',
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
Built with the DeepSeek Harness SDK using the {{model}} model.
|
||||
|
||||
{{#if isAcp}}
|
||||
## Run as an ACP server
|
||||
## Run as an ACP automation server
|
||||
|
||||
Run `{{packageManager}} start` and configure your ACP client to launch this project. Standard output is reserved for ACP JSON-RPC.
|
||||
Run `{{packageManager}} start` and configure a programmatic ACP client to launch this project. Standard output is reserved for ACP JSON-RPC.
|
||||
{{else}}
|
||||
{{#if isTui}}
|
||||
## Run in a terminal
|
||||
|
||||
@@ -289,12 +289,13 @@ describe('SdkProject and ProjectEditSession', () => {
|
||||
edit.configureFeature(registry.get(featureId('app')), selection('app', ['acp']))
|
||||
const acp = (await edit.commit()).project
|
||||
expect(acp.profile.runInterface).toBe('acp')
|
||||
expect(acp.cordis.entry('commands')).toMatchObject({ name: '@deepseek-ai/dsh-commands' })
|
||||
expect(acp.cordis.entry('commands')).toBeUndefined()
|
||||
expect(acp.cordis.entry('user-interaction')).toBeUndefined()
|
||||
expect(acp.packageManifest().scripts).toMatchObject({
|
||||
dev: 'dsh-sdk dev index.ts',
|
||||
start: 'dsh-sdk start index.js',
|
||||
})
|
||||
expect(await readFile(join(acp.root, 'README.md'), 'utf8')).toContain('Run as an ACP server')
|
||||
expect(await readFile(join(acp.root, 'README.md'), 'utf8')).toContain('Run as an ACP automation server')
|
||||
expect(await readFile(join(acp.root, 'index.ts'), 'utf8')).not.toContain('agents.create')
|
||||
|
||||
const acpRegistry = createBuiltinRegistry(acp.profile)
|
||||
@@ -324,12 +325,16 @@ describe('SdkProject and ProjectEditSession', () => {
|
||||
.toContain('missing package.json script dev')
|
||||
})
|
||||
|
||||
it('rejects enabled features that do not apply to the target app interface', async () => {
|
||||
it('rejects ask-user on non-interactive app interfaces', async () => {
|
||||
const project = await createCommitted([selection('ask-user', ['default'])])
|
||||
const registry = createBuiltinRegistry(project.profile)
|
||||
const edit = project.edit(registry)
|
||||
edit.configureFeature(registry.get(featureId('app')), selection('app', ['embed']))
|
||||
await expect(edit.commit()).rejects.toThrow('feature ask-user is not available for embed')
|
||||
const embed = project.edit(registry)
|
||||
embed.configureFeature(registry.get(featureId('app')), selection('app', ['embed']))
|
||||
await expect(embed.commit()).rejects.toThrow('feature ask-user is not available for embed')
|
||||
|
||||
const acp = project.edit(registry)
|
||||
acp.configureFeature(registry.get(featureId('app')), selection('app', ['acp']))
|
||||
await expect(acp.commit()).rejects.toThrow('feature ask-user is not available for acp')
|
||||
})
|
||||
|
||||
it('supports disabled feature reconfiguration and rejects invalid state operations', async () => {
|
||||
|
||||
@@ -85,7 +85,7 @@ Change file: package.json
|
||||
"choices": [
|
||||
{
|
||||
"default": false,
|
||||
"label": "ACP server",
|
||||
"label": "ACP automation server",
|
||||
"value": "acp",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -542,23 +542,23 @@ describe('ConfigWorkflow', () => {
|
||||
expect(result.commit?.project.cordis.entry('agent-core')).toBeUndefined()
|
||||
})
|
||||
|
||||
it('disables ask-user when switching its app interface to embed', async () => {
|
||||
it('disables ask-user when switching its app interface to ACP', async () => {
|
||||
const project = await committedProject([
|
||||
{ id: featureId('ask-user'), options: ['default'] },
|
||||
], [], 'acp')
|
||||
], [], 'tui')
|
||||
const registry = createBuiltinRegistry(project.profile)
|
||||
const output = outputBuffer()
|
||||
const workflow = new ConfigWorkflow(new QueuePort([
|
||||
[
|
||||
{ value: 'feature:provider', choices: ['deepseek'] },
|
||||
{ value: 'feature:app', choices: ['embed'] },
|
||||
{ value: 'feature:app', choices: ['acp'] },
|
||||
{ value: 'feature:persistence', choices: ['jsonl'] },
|
||||
{ value: 'feature:ask-user', choices: ['default'] },
|
||||
],
|
||||
true,
|
||||
]), output.stream, async () => {})
|
||||
const result = await workflow.run(project, registry)
|
||||
expect(result.commit?.project.profile.runInterface).toBe('embed')
|
||||
expect(result.commit?.project.profile.runInterface).toBe('acp')
|
||||
expect(result.commit?.project.cordis.entry('tool-ask-user')?.disabled).toBe(true)
|
||||
expect(output.read()).toContain('Disable feature: ask-user')
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user