Merge remote-tracking branch 'origin/master' into codex/agent-session-jsonl-location

# Conflicts:
#	docs/architecture.md
#	docs/capability-seams.md
#	docs/config-catalog.md
#	docs/cordis-catalog/services.md
#	docs/core-data-structures/bash.md
#	docs/module-graph.md
#	docs/tool-catalog.md
#	examples/acp-agent/tests/snapshots/advanced-toolchain/system-prompt.golden.md
#	examples/acp-agent/tests/snapshots/advanced-toolchain/tool-schemas.golden.json
#	examples/acp-agent/tests/snapshots/both-mode-turn/system-prompt.golden.md
#	examples/acp-agent/tests/snapshots/both-mode-turn/tool-schemas.golden.json
#	examples/acp-agent/tests/snapshots/code-mode-turn/system-prompt.golden.md
#	examples/acp-agent/tests/snapshots/permission-switching/tool-schemas.golden.json
#	examples/acp-agent/tests/snapshots/skill-load/tool-schemas.golden.json
#	examples/acp-agent/tests/snapshots/text-turn/tool-schemas.golden.json
#	examples/acp-agent/tests/snapshots/workspace-edit/tool-schemas.golden.json
#	packages/bash/bash-local/README.md
#	packages/bash/bash-local/src/index.ts
#	packages/bash/bash-local/tests/executor.spec.ts
#	packages/bash/bash/README.md
#	packages/bash/bash/src/index.ts
#	packages/bash/bash/src/types.ts
#	packages/bash/tool-bash/README.md
#	packages/bash/tool-bash/package.json
#	packages/bash/tool-bash/src/index.ts
#	packages/bash/tool-bash/tests/tools.spec.ts
#	packages/bash/tool-bash/tsconfig.json
#	packages/cordis/tool-cordis/src/api-catalog.ts
#	packages/examples/agent-spine-demo/README.md
#	packages/examples/agent-spine-demo/src/index.ts
#	packages/examples/agent-spine-demo/tests/agent-core.spec.ts
#	pnpm-lock.yaml
This commit is contained in:
Yichen Jiang
2026-07-16 16:45:52 +08:00
243 changed files with 17079 additions and 2872 deletions

View File

@@ -1,8 +1,8 @@
import { describe, expect, it } from 'vitest'
import { describe, expect, it, vi } from 'vitest'
import { mkdir, mkdtemp, writeFile } from 'node:fs/promises'
import { join } from 'node:path'
import { tmpdir } from 'node:os'
import { Context, Service } from 'cordis'
import { Context } from 'cordis'
import Loader from '@cordisjs/plugin-loader'
import { TOOL_ORDER_REST } from '@deepseek-ai/dsh-system-prompt'
import * as agentCore from '../src/index.ts'
@@ -10,14 +10,9 @@ import { AgentId, agentEvents, type Agent } from '@deepseek-ai/dsh-agent'
import { CallId, type Message } from '@deepseek-ai/dsh-llm'
import type { ToolExecution } from '@deepseek-ai/dsh-tools'
/** Minimal service that lets the executor-less bundle activate tool-bash in config-forwarding tests. */
class StubBashService extends Service {
constructor(ctx: Context) {
super(ctx, 'bash')
}
onTaskDone(): () => void {
return () => undefined
declare module '@deepseek-ai/dsh-tasks' {
interface TaskKindMap {
probe: 'probe'
}
}
@@ -40,12 +35,13 @@ async function composePrefix(ctx: Context, cwd: string): Promise<Message[]> {
* Loader-path guard (export shape, `unwrapExports`) is the app packages' keyless
* bin smokes; here we assert the composition + config forwarding.
*/
async function mount(config?: agentCore.Config): Promise<Context> {
async function mount(config?: agentCore.Config, withBash = false): Promise<Context> {
const oldDshHome = process.env.DSH_HOME
const oldAgentsHome = process.env.DSH_AGENTS_HOME
process.env.DSH_HOME = await mkdtemp(join(tmpdir(), 'dsh-agent-spine-demo-home-'))
process.env.DSH_AGENTS_HOME = await mkdtemp(join(tmpdir(), 'dsh-agent-spine-demo-agents-'))
const ctx = new Context()
if (withBash) ctx.provide('bash', { sandboxMode: undefined })
try {
await ctx.plugin(agentCore, config)
// The bundle mounts its children inside apply() (not awaited there); let their
@@ -98,6 +94,7 @@ describe('dsh-agent-spine-demo bundle', () => {
expect(ctx.get('tools')).toBeDefined()
expect(ctx.get('skills')).toBeDefined()
expect(ctx.get('agents')).toBeDefined()
expect(ctx.get('tasks')).toBeDefined()
expect(ctx.get('agentLoop')).toBeDefined()
await ctx.fiber.dispose()
})
@@ -171,13 +168,10 @@ describe('dsh-agent-spine-demo bundle', () => {
await mkdir(join(home, 'skills'), { recursive: true })
await writeFile(join(home, 'skills', 'shared-skill.md'), '---\nname: shared-skill\ndescription: Shared home skill\n---\n\nShared body.\n')
const ctx = new Context()
await ctx.plugin(StubBashService)
await ctx.plugin(agentCore, {
const ctx = await mount({
dshHome: home,
skills: { local: { agentsHome } },
})
await new Promise(resolve => setTimeout(resolve, 50))
}, true)
expect((await ctx.skills.list()).map(skill => skill.name)).toEqual(['shared-skill'])
const execution: ToolExecution = {
@@ -199,6 +193,33 @@ describe('dsh-agent-spine-demo bundle', () => {
}).toThrow(/must resolve to the same directory/)
})
it('forwards its bundled tool configs to tool-bash and tool-tasks', async () => {
const ctx = await mount({
toolBash: { enableRunInBackground: false },
toolTasks: { waitTimeoutMs: 7, maxWaitTimeoutMs: 11 },
}, true)
const bash = ctx.tools.schemas().find(tool => tool.name === 'bash')
expect(bash).toBeDefined()
expect(Object.keys((bash!.parameters as { properties: Record<string, unknown> }).properties))
.not.toContain('run_in_background')
const id = ctx.tasks.start({
kind: 'probe',
label: 'config forwarding probe',
run: () => ({ cancel: () => {}, done: Promise.resolve({ status: 'completed' }) }),
})
const wait = vi.spyOn(ctx.tasks, 'wait')
await ctx.tools.execute({
callId: CallId('task-config-forwarding'),
name: 'task_output',
arguments: { task_id: id, wait: true },
})
expect(wait).toHaveBeenCalledWith(id, 7, undefined, undefined)
await ctx.fiber.dispose()
})
it('uses the default skill config when apply is called directly without skills', async () => {
await withIsolatedSkillHomes(async () => {
const ctx = new Context()
@@ -223,7 +244,7 @@ describe('dsh-agent-spine-demo bundle', () => {
})
}
const assembly = await ctx.get('systemPrompt')!.assemble()
expect(assembly.tools.map(tool => tool.name)).toEqual(['zulu', 'alpha', 'skill'])
expect(assembly.tools.map(tool => tool.name)).toEqual(['zulu', 'alpha', 'skill', 'task_kill', 'task_list', 'task_output'])
await ctx.fiber.dispose()
})