Merge remote-tracking branch 'origin/master' into codex/project-instruction-files

# Conflicts:
#	docs/config-catalog.md
#	docs/cordis-catalog/events.md
#	docs/cordis-catalog/services.md
#	docs/core-data-structures/core.md
#	docs/event-producer-consumer.md
#	docs/persistence-catalog.md
#	docs/rfc/implemented/feature/2026-06-15-code-mode.md
#	docs/rfc/implemented/feature/2026-06-30-interception-seams.md
#	docs/rfc/proposed/simplification/2026-07-04-prune-dead-core-spine-surface.md
#	docs/tool-execution-pipeline.md
#	packages/cordis/tool-cordis/src/api-catalog.ts
#	packages/core/agent-core/tests/agent-core.spec.ts
#	packages/core/agent-loop/src/agent.ts
#	packages/core/agent/README.md
#	packages/core/session/src/index.ts
#	packages/core/tools/README.md
#	packages/core/tools/src/index.ts
#	pnpm-lock.yaml
#	scripts/gen-doc-graphs.ts
#	scripts/type-equiv.manifest.json
This commit is contained in:
Yichen Jiang
2026-07-13 14:37:32 +08:00
243 changed files with 14538 additions and 4798 deletions

View File

@@ -45,7 +45,7 @@ async function harness(): Promise<{ ctx: Context; agent: Agent }> {
await ctx.plugin(WorkspaceContext, { maxBytes: 65536 })
await ctx.plugin(AgentLoop, { agents: [] })
await ctx.plugin(LlmDeepSeek, { models: ['deepseek-v4-flash'] })
const handle = ctx.agents.create({
const handle = await ctx.agents.create({
agentId: AgentId('workspace-context-e2e'),
sessionId: SessionId('workspace-context-e2e-session'),
meta: { cwd: workdir },

View File

@@ -23,6 +23,7 @@ import type {
import LocalFileSystem from '@deepseek-ai/dsh-fs-local'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry from '@deepseek-ai/dsh-tools'
import type { ToolExecution, ToolExecutionToken } from '@deepseek-ai/dsh-tools'
import * as ToolFs from '@deepseek-ai/dsh-tool-fs'
import {
discoverBaselineInstructionFiles,
@@ -116,6 +117,7 @@ function stubAgent(cwd?: string, seed: SessionEvent[] = []): Agent {
const id = SessionId('s1')
const session = new Session(id, seed, cwd === undefined ? undefined : { version: SESSION_FORMAT_VERSION, id, createdAt: 0, cwd })
return {
ctx: new Context(),
id: AgentId('a1'),
options: {},
session,
@@ -135,6 +137,13 @@ function stubAgent(cwd?: string, seed: SessionEvent[] = []): Agent {
}
}
function stubToolExecution(input: Omit<ToolExecution, 'token'>): ToolExecution {
return {
token: Symbol('workspace-context-test-execution') as ToolExecutionToken,
...input,
}
}
function blocksText(blocks: { type: string; text?: string }[] | undefined): string {
return blocks?.map(block => block.type === 'text' ? block.text ?? '' : '').join('\n') ?? ''
}
@@ -712,12 +721,12 @@ describe('workspace context request injection', () => {
try {
await ctx.plugin(workspaceContext, { maxBytes: 65536 })
const decision = await ctx.waterfall('tools/post-execute', {
const decision = await ctx.waterfall('tools/post-execute', stubToolExecution({
callId: CallId('no-fs-post-execute'),
name: 'read',
arguments: { file_path: 'pkg/file.txt' },
agent: stubAgent('/virtual/repo'),
}, {
}), {
callId: CallId('no-fs-post-execute'),
isError: false,
content: [{ type: 'text', text: 'file content' }],
@@ -748,12 +757,12 @@ describe('workspace context request injection', () => {
await ctx.plugin(workspaceContext, { dshHome: home, maxBytes: 65536 })
const agent = stubAgent(root)
const exec = {
const exec = stubToolExecution({
callId: CallId('read-blocked-post-execute'),
name: 'read',
arguments: { file_path: 'pkg/file.txt' },
agent,
}
})
const result = {
callId: CallId('read-blocked-post-execute'),
isError: false,
@@ -1951,14 +1960,14 @@ describe('dynamic nested workspace context injection', () => {
isError: false,
}
const failedStat = await ctx.waterfall('tools/post-execute', {
const failedStat = await ctx.waterfall('tools/post-execute', stubToolExecution({
callId: CallId('provider-stat-failure'), name: 'read', arguments: { file_path: 'pkg/file.txt' }, agent,
}, result, async () => ({ kind: 'accept' as const }))
}), result, async () => ({ kind: 'accept' as const }))
fs.throwOnStat.clear()
fs.entries.set(join(root, 'pkg/AGENTS.md'), { type: 'directory' })
const mismatchedStat = await ctx.waterfall('tools/post-execute', {
const mismatchedStat = await ctx.waterfall('tools/post-execute', stubToolExecution({
callId: CallId('provider-stat-mismatch'), name: 'read', arguments: { file_path: 'pkg/file.txt' }, agent,
}, result, async () => ({ kind: 'accept' as const }))
}), result, async () => ({ kind: 'accept' as const }))
expect(failedStat).toEqual({ kind: 'accept' })
expect(mismatchedStat).toEqual({ kind: 'accept' })
@@ -2102,12 +2111,12 @@ describe('dynamic nested workspace context injection', () => {
]
for (const item of cases) {
const decision = await ctx.waterfall('tools/post-execute', {
const decision = await ctx.waterfall('tools/post-execute', stubToolExecution({
callId: CallId(`manual-${item.name}-${cases.indexOf(item)}`),
name: item.name,
arguments: item.arguments,
...item.agent === undefined ? {} : { agent: item.agent },
}, result, async () => ({ kind: 'accept' as const }))
}), result, async () => ({ kind: 'accept' as const }))
expect(decision).toEqual({ kind: 'accept' })
}
} finally {