Merge latest master into invariant service seam

# Conflicts:
#	.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.md
#	docs/capability-seams.md
#	docs/event-producer-consumer.md
#	packages/examples/agent-spine-demo/README.md
#	packages/examples/stdio-demo/tests/built-bin.e2e.ts
#	scripts/gen-doc-graphs.ts
This commit is contained in:
Tianyi Cui
2026-07-20 20:58:46 +08:00
175 changed files with 1343 additions and 4271 deletions

View File

@@ -29,7 +29,7 @@ const ID = featureId('app')
function appProjectResources(
profile: ProjectProfile,
runInterface: 'acp' | 'stdio' | 'embed',
runInterface: 'acp' | 'tui' | 'embed',
): readonly ProjectResource[] {
const context = createProjectTemplateContext(profile, runInterface)
const scripts = createAppPackageScripts(context)
@@ -43,10 +43,10 @@ function appProjectResources(
}
class AppOption extends FeatureOption {
override readonly id: 'acp' | 'stdio' | 'embed'
override readonly id: 'acp' | 'tui' | 'embed'
override readonly label: string
constructor(id: 'acp' | 'stdio' | 'embed', label: string) {
constructor(id: 'acp' | 'tui' | 'embed', label: string) {
super()
this.id = id
this.label = label
@@ -56,7 +56,7 @@ class AppOption extends FeatureOption {
override markerConfigEntries(): readonly { id: string; name: string }[] {
switch (this.id) {
case 'acp': return [{ id: 'acp', name: '@deepseek-ai/dsh-acp' }]
case 'stdio': return [{ id: 'stdio', name: '@deepseek-ai/dsh-stdio' }]
case 'tui': return [{ id: 'tui', name: '@deepseek-ai/dsh-tui' }]
case 'embed': return []
}
}
@@ -65,7 +65,7 @@ class AppOption extends FeatureOption {
override matchesConfigEntries(entries: readonly { id: string; name: string }[], profile: ProjectProfile): boolean {
if (this.id !== 'embed') return super.matchesConfigEntries(entries, profile)
return entries.some(entry => entry.id === 'agent-loop' && entry.name === '@deepseek-ai/dsh-agent-loop')
&& !entries.some(entry => entry.name === '@deepseek-ai/dsh-acp' || entry.name === '@deepseek-ai/dsh-stdio')
&& !entries.some(entry => entry.name === '@deepseek-ai/dsh-acp' || entry.name === '@deepseek-ai/dsh-tui')
}
override contribution(profile: ProjectProfile): ProjectContribution {
@@ -83,7 +83,7 @@ class AppOption extends FeatureOption {
config: { model: profile.runtime.model },
}, ['model'], config => requiredString(config, 'model')),
])
case 'stdio':
case 'tui':
return new ProjectContribution([
...appProjectResources(profile, this.id),
...npmCordisConfigEntry(ID, {
@@ -91,10 +91,10 @@ class AppOption extends FeatureOption {
name: '@deepseek-ai/dsh-user-interaction',
}),
...npmCordisConfigEntry(ID, {
id: 'stdio',
name: '@deepseek-ai/dsh-stdio',
id: 'tui',
name: '@deepseek-ai/dsh-tui',
config: {
welcome: 'agent REPL ready. Give it a coding task.',
welcome: 'TUI agent ready. Give it a coding task.',
sessionId: new JsExpression('process.env.DSH_SDK_SESSION_ID'),
},
}, ['welcome', 'sessionId'], config => [
@@ -108,7 +108,7 @@ class AppOption extends FeatureOption {
}
}
/** Required app selection represented by acp, stdio, or embed options. */
/** Required app selection represented by ACP, TUI, or embed options. */
export class AppFeature extends ExclusiveOptionFeature {
override readonly id = ID
override readonly summary = 'Run interface'
@@ -116,7 +116,7 @@ export class AppFeature extends ExclusiveOptionFeature {
override readonly requires = [featureId('spine')]
override readonly options = [
new AppOption('acp', 'ACP server'),
new AppOption('stdio', 'Terminal REPL'),
new AppOption('tui', 'Terminal TUI'),
new AppOption('embed', 'Embedded context'),
]

View File

@@ -347,7 +347,7 @@ config:
id: 'ask-user',
summary: 'Ask the user from the model loop',
mode: 'single',
supportedInterfaces: ['acp', 'stdio'],
supportedInterfaces: ['acp', 'tui'],
options: [{
id: 'default',
label: 'ask_user_question tool',

View File

@@ -250,7 +250,7 @@ class DefinedFeature extends Feature {
this.required = spec.required ?? false
this.requires = (spec.requires ?? []).map(requirement => featureId(requirement.id))
this.suggests = (spec.suggests ?? []).map(featureId)
this.supportedInterfaces = spec.supportedInterfaces ?? ['acp', 'stdio', 'embed']
this.supportedInterfaces = spec.supportedInterfaces ?? ['acp', 'tui', 'embed']
}
override defaultOptions(): readonly string[] {

View File

@@ -113,7 +113,7 @@ export abstract class Feature {
/** Features recommended during creation. */
readonly suggests: readonly FeatureId[] = []
/** Front doors under which this feature is meaningful. */
readonly supportedInterfaces: readonly RunInterface[] = ['acp', 'stdio', 'embed']
readonly supportedInterfaces: readonly RunInterface[] = ['acp', 'tui', 'embed']
/**
* Options selected when installation has no override.

View File

@@ -549,7 +549,7 @@ export class ProjectEditSession implements FeatureProjectView {
private finalProfile(): ProjectProfile {
const runInterface = this.states.get(featureId('app'))?.selection?.options[0]
if (runInterface !== 'acp' && runInterface !== 'stdio' && runInterface !== 'embed') return this.profile
if (runInterface !== 'acp' && runInterface !== 'tui' && runInterface !== 'embed') return this.profile
return { ...this.profile, runInterface }
}

View File

@@ -42,7 +42,7 @@ const OPTIONAL_DOCUMENTS = [
function runInterface(entries: readonly CordisConfigEntry[]): RunInterface {
if (entries.some(entry => entry.name === '@deepseek-ai/dsh-acp')) return 'acp'
if (entries.some(entry => entry.name === '@deepseek-ai/dsh-stdio')) return 'stdio'
if (entries.some(entry => entry.name === '@deepseek-ai/dsh-tui')) return 'tui'
return 'embed'
}
@@ -146,7 +146,7 @@ export class SdkProject {
static create(root: string, request: ProjectCreationRequest): SdkProject {
const app = request.features.find(selection => selection.id === 'app')
const selectedInterface = app?.options[0]
if (selectedInterface !== 'acp' && selectedInterface !== 'stdio' && selectedInterface !== 'embed') {
if (selectedInterface !== 'acp' && selectedInterface !== 'tui' && selectedInterface !== 'embed') {
throw new Error('project creation requires one app feature option')
}
const profile: ProjectProfile = {

View File

@@ -9,7 +9,7 @@ import type { LocalPluginBlueprint } from '../plugins/local-plugin-blueprint.ts'
import type { FeatureId } from '../ids.ts'
/** Runtime front door selected for a generated project. */
export type RunInterface = 'acp' | 'stdio' | 'embed'
export type RunInterface = 'acp' | 'tui' | 'embed'
/** Values shared by the required provider and app features. */
interface ProjectRuntimeOptions {

View File

@@ -9,7 +9,7 @@ Built with the DeepSeek Harness SDK using the {{model}} model.
Run `{{packageManager}} start` and configure your ACP client to launch this project. Standard output is reserved for ACP JSON-RPC.
{{else}}
{{#if isStdio}}
{{#if isTui}}
## Run in a terminal
Run `{{packageManager}} start` to start the interactive agent.

View File

@@ -8,18 +8,18 @@ import { startSDK, type SdkBootContext } from '@deepseek-ai/dsh-scripts'
/** Boot this project's cordis.yml when invoked by dsh-scripts. */
export async function main(boot: SdkBootContext) {
{{#if isStdio}}
{{#if isTui}}
const model = boot.args.model
if (typeof model !== 'string' || model.length === 0) throw new Error('stdio startup requires --model=<name>')
if (typeof model !== 'string' || model.length === 0) throw new Error('TUI startup requires --model=<name>')
const resume = boot.args.resume
if (resume !== undefined && (typeof resume !== 'string' || resume.length === 0)) {
throw new Error('stdio startup requires --resume=<session-id>')
throw new Error('TUI startup requires --resume=<session-id>')
}
const sessionId = SessionId(resume ?? `main-session-${randomUUID()}`)
process.env.DSH_SDK_SESSION_ID = sessionId
{{/if}}
const ctx = await startSDK(new URL('./cordis.yml', import.meta.url))
{{#if isStdio}}
{{#if isTui}}
try {
if (resume === undefined) {
await ctx.agents.create({
@@ -37,7 +37,7 @@ export async function main(boot: SdkBootContext) {
try {
await ctx.fiber.dispose()
} catch (disposeError) {
throw new AggregateError([error, disposeError], 'stdio startup and cleanup failed')
throw new AggregateError([error, disposeError], 'TUI startup and cleanup failed')
}
throw error
}

View File

@@ -20,7 +20,7 @@ export interface ProjectTemplateContext {
model: string
modelLiteral: string
isAcp: boolean
isStdio: boolean
isTui: boolean
isEmbed: boolean
packageManager: PackageManagerName
installArgs: string
@@ -60,7 +60,7 @@ export function createProjectTemplateContext(
model: profile.runtime.model,
modelLiteral: JSON.stringify(profile.runtime.model),
isAcp: runInterface === 'acp',
isStdio: runInterface === 'stdio',
isTui: runInterface === 'tui',
isEmbed: runInterface === 'embed',
packageManager: profile.packageManager.name,
installArgs: profile.packageManager.installCommand().join(' '),
@@ -105,7 +105,7 @@ export function createAppProjectArtifacts(
/** Build package scripts owned by the selected app feature option. */
export function createAppPackageScripts(context: ProjectTemplateContext): Readonly<Record<'dev' | 'start', string>> {
const modelArg = context.isStdio ? ` -- --model=${JSON.stringify(context.model)}` : ''
const modelArg = context.isTui ? ` -- --model=${JSON.stringify(context.model)}` : ''
return {
dev: `dsh-sdk dev index.ts${modelArg}`,
start: `dsh-sdk start index.js${modelArg}`,