Merge refreshed rfc/pty into feature/persistent-pty-sessions

# Conflicts:
#	docs/architecture.md
#	docs/capability-seams.md
#	docs/config-catalog.md
#	docs/cordis-catalog/services.md
#	docs/module-graph.md
#	docs/tool-catalog.md
#	examples/acp-agent/tests/acp.snapshot.ts
#	examples/headless-agent/tests/headless.snapshot.ts
#	examples/package.json
#	packages/README.md
#	packages/cordis/tool-cordis/src/api-catalog.ts
#	packages/core/tools/tests/gen-tool-catalog.spec.ts
#	pnpm-lock.yaml
#	pnpm-workspace.yaml
#	scripts/gen-tool-catalog.ts
#	scripts/type-equiv.manifest.json
#	website/.vitepress/config/api-sidebar.json
This commit is contained in:
Tianyi Cui
2026-07-22 21:12:16 +08:00
1856 changed files with 106649 additions and 23384 deletions

View File

@@ -11,11 +11,16 @@
"types": "./lib/types/index.d.ts",
"default": "./lib/index.js"
},
"./invariant": {
"types": "./lib/types/invariant.d.ts",
"default": "./lib/invariant.js"
},
"./src/*": "./src/*",
"./package.json": "./package.json"
},
"files": [
"lib/index.js",
"lib/invariant.js",
"lib/types/**/*.d.ts",
"lib/types/**/*.d.ts.map",
"src"
@@ -25,6 +30,7 @@
},
"license": "BSD-3-Clause",
"peerDependencies": {
"@deepseek-ai/dsh-invariants": "^0.0.1",
"@deepseek-ai/dsh-pty": "^0.0.1",
"@deepseek-ai/dsh-sandbox": "^0.0.1",
"@deepseek-ai/dsh-sandbox-policy": "^0.0.1",
@@ -36,6 +42,7 @@
},
"devDependencies": {
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-pty": "workspace:^",
"@deepseek-ai/dsh-sandbox": "workspace:^",
"@deepseek-ai/dsh-sandbox-policy": "workspace:^",

View File

@@ -0,0 +1,30 @@
/**
* Package-owned invariant companion for `@deepseek-ai/dsh-pty-local`.
* @module @deepseek-ai/dsh-pty-local/invariant
*/
/* jscpd:ignore-start */
import type { Context } from 'cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-pty-local'
/** Cordis companion plugin name. */
export const name = 'pty-local-invariant'
/** Service required before the companion can reserve package ownership. */
export const inject = ['invariants']
/**
* No runtime invariant: readiness, terminal buffers, and process-tree state are private per-session
* implementation state, and the backend publishes no independent lifecycle stream or snapshot.
*/
const install: InvariantInstaller = () => {}
/**
* Register this package's invariant companion.
* @param ctx - Cordis context carrying the invariant service.
* @returns the installed registration's disposer after setup succeeds.
*/
export const apply = (ctx: Context): Promise<() => void> =>
Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install))
/* jscpd:ignore-end */

View File

@@ -25,6 +25,9 @@
},
{
"path": "../../sandbox/sandbox-policy"
},
{
"path": "../../support/invariants"
}
]
}

View File

@@ -11,11 +11,16 @@
"types": "./lib/types/index.d.ts",
"default": "./lib/index.js"
},
"./invariant": {
"types": "./lib/types/invariant.d.ts",
"default": "./lib/invariant.js"
},
"./src/*": "./src/*",
"./package.json": "./package.json"
},
"files": [
"lib/index.js",
"lib/invariant.js",
"lib/types/**/*.d.ts",
"lib/types/**/*.d.ts.map",
"src"
@@ -24,11 +29,13 @@
"peerDependencies": {
"@deepseek-ai/dsh-agent": "^0.0.1",
"@deepseek-ai/dsh-brand": "^0.0.1",
"@deepseek-ai/dsh-invariants": "^0.0.1",
"cordis": "^4.0.0-rc.7"
},
"devDependencies": {
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-brand": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"cordis": "^4.0.0-rc.7"
}

View File

@@ -0,0 +1,30 @@
/**
* Package-owned invariant companion for `@deepseek-ai/dsh-pty`.
* @module @deepseek-ai/dsh-pty/invariant
*/
/* jscpd:ignore-start */
import type { Context } from 'cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-pty'
/** Cordis companion plugin name. */
export const name = 'pty-invariant'
/** Service required before the companion can reserve package ownership. */
export const inject = ['invariants']
/**
* No runtime invariant: backend and owner-scoped session registries are private mutable state,
* and the service exposes neither an independent lifecycle stream nor an unscoped snapshot.
*/
const install: InvariantInstaller = () => {}
/**
* Register this package's invariant companion.
* @param ctx - Cordis context carrying the invariant service.
* @returns the installed registration's disposer after setup succeeds.
*/
export const apply = (ctx: Context): Promise<() => void> =>
Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install))
/* jscpd:ignore-end */

View File

@@ -19,6 +19,9 @@
},
{
"path": "../../util/brand"
},
{
"path": "../../support/invariants"
}
]
}

View File

@@ -11,11 +11,16 @@
"types": "./lib/types/index.d.ts",
"default": "./lib/index.js"
},
"./invariant": {
"types": "./lib/types/invariant.d.ts",
"default": "./lib/invariant.js"
},
"./src/*": "./src/*",
"./package.json": "./package.json"
},
"files": [
"lib/index.js",
"lib/invariant.js",
"lib/types/**/*.d.ts",
"lib/types/**/*.d.ts.map",
"src"
@@ -23,6 +28,7 @@
"license": "BSD-3-Clause",
"peerDependencies": {
"@deepseek-ai/dsh-agent": "^0.0.1",
"@deepseek-ai/dsh-invariants": "^0.0.1",
"@deepseek-ai/dsh-llm": "^0.0.1",
"@deepseek-ai/dsh-pty": "^0.0.1",
"@deepseek-ai/dsh-system-prompt": "^0.0.1",
@@ -34,6 +40,7 @@
"@cordisjs/plugin-include": "workspace:^",
"@cordisjs/plugin-loader": "workspace:^",
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-pty": "workspace:^",
"@deepseek-ai/dsh-pty-local": "workspace:^",

View File

@@ -125,7 +125,6 @@ export function apply(ctx: Context): void {
if (args.run_in_background === true) {
const tasks = ctx.get('tasks')
if (tasks === undefined) throw new Error('background terminal sends require @deepseek-ai/dsh-tasks and @deepseek-ai/dsh-tool-tasks')
if (exec.signal?.aborted === true) throw new Error('terminal send aborted')
let cancelRequested = false
const taskId = tasks.start({
kind: 'pty-send',
@@ -148,9 +147,9 @@ export function apply(ctx: Context): void {
})
return { content: textResult(`started background task ${taskId}`), isError: false }
}
const operation = ctx.pty.startSend(owner, id, { ...request, ...exec.signal ? { signal: exec.signal } : {} })
const operation = ctx.pty.startSend(owner, id, { ...request, signal: exec.signal })
const result = await operation.done
if (exec.signal?.aborted === true) throw new Error('terminal send aborted')
if (exec.signal.aborted) throw new Error('terminal send aborted')
return { content: textResult(renderSend(result)), isError: false, meta: result }
},
presentCall(args) {

View File

@@ -0,0 +1,30 @@
/**
* Package-owned invariant companion for `@deepseek-ai/dsh-tool-pty`.
* @module @deepseek-ai/dsh-tool-pty/invariant
*/
/* jscpd:ignore-start */
import type { Context } from 'cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-tool-pty'
/** Cordis companion plugin name. */
export const name = 'tool-pty-invariant'
/** Service required before the companion can reserve package ownership. */
export const inject = ['invariants']
/**
* No runtime invariant: this stateless adapter contributes tools and prompt guidance, while PTY
* lifecycle and background-task relationships remain owned by the services it composes.
*/
const install: InvariantInstaller = () => {}
/**
* Register this package's invariant companion.
* @param ctx - Cordis context carrying the invariant service.
* @returns the installed registration's disposer after setup succeeds.
*/
export const apply = (ctx: Context): Promise<() => void> =>
Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install))
/* jscpd:ignore-end */

View File

@@ -102,16 +102,17 @@ suite('terminal real Loader composition through cordis.yml', () => {
await context.loader.await()
const owner = agent(context)
const signal = new AbortController().signal
const spawn = await context.tools.execute({
callId: CallId('spawn'), name: 'terminal_open', arguments: { type: 'shell', name: 'main', cwd: root }, agent: owner,
signal, callId: CallId('spawn'), name: 'terminal_open', arguments: { type: 'shell', name: 'main', cwd: root }, agent: owner,
})
expect(resultText(spawn)).toContain('started terminal session pty-1 (main)')
await context.tools.execute({
callId: CallId('state'), name: 'terminal_send', arguments: { sessionId: 'pty-1', text: 'export KEEP=loader; cd /' }, agent: owner,
signal, callId: CallId('state'), name: 'terminal_send', arguments: { sessionId: 'pty-1', text: 'export KEEP=loader; cd /' }, agent: owner,
})
const read = await context.tools.execute({
callId: CallId('read'), name: 'terminal_send', arguments: { sessionId: 'pty-1', text: 'printf "cwd=%s keep=%s\\n" "$PWD" "$KEEP"' }, agent: owner,
signal, callId: CallId('read'), name: 'terminal_send', arguments: { sessionId: 'pty-1', text: 'printf "cwd=%s keep=%s\\n" "$PWD" "$KEEP"' }, agent: owner,
})
expect(resultText(read)).toContain('cwd=/ keep=loader')
expect(context.pty.list(owner)).toHaveLength(1)

View File

@@ -104,8 +104,9 @@ async function setup(tasks: boolean) {
}
let callNumber = 0
const testToolSignal = new AbortController().signal
function call(ctx: Context, name: string, args: unknown, agent?: Agent) {
return ctx.tools.execute({ callId: CallId(`pty-call-${++callNumber}`), name, arguments: args, ...agent ? { agent } : {} })
return ctx.tools.execute({ signal: testToolSignal, callId: CallId(`pty-call-${++callNumber}`), name, arguments: args, ...agent ? { agent } : {} })
}
function callWithSignal(ctx: Context, name: string, args: unknown, agent: Agent, signal: AbortSignal) {

View File

@@ -31,6 +31,9 @@
},
{
"path": "../../tasks/tasks"
},
{
"path": "../../support/invariants"
}
]
}