Merge remote-tracking branch 'origin/master' into codex/pr224-rfc-rewrite

# Conflicts:
#	docs/architecture.md
#	docs/capability-seams.md
#	docs/config-catalog.md
#	docs/cookbook/extension-cookbook.md
#	docs/cordis-catalog/events.md
#	docs/cordis-catalog/services.md
#	docs/event-producer-consumer.md
#	docs/module-graph.md
#	docs/rfc/implemented/feature/2026-06-30-interception-seams.md
#	docs/rfc/proposed/feature/2026-06-14-acp-agent-client-protocol.md
#	docs/tool-execution-pipeline.md
#	packages/cordis/tool-cordis/src/api-catalog.ts
#	packages/core/agent-core/README.md
#	packages/core/agent-loop/README.md
#	packages/core/tools/README.md
#	packages/core/tools/src/index.ts
#	packages/core/tools/tests/tools.spec.ts
#	packages/core/tools/tsconfig.json
#	packages/ui/acp/src/index.ts
#	scripts/doc-budgets.manifest.json
#	scripts/gen-cordis-catalog.ts
#	scripts/gen-doc-graphs.ts
This commit is contained in:
Tianyi Cui
2026-07-11 23:14:09 +08:00
185 changed files with 11901 additions and 414 deletions
+11 -7
View File
@@ -15,7 +15,7 @@ import type { Scope } from '@deepseek-ai/dsh-scope'
import { agentEvents } from '@deepseek-ai/dsh-agent'
import type { AgentFactory, AgentHandle, AgentId, AgentOptions, CreateAgentOptions, ResumeAgentOptions, SessionStartSource } from '@deepseek-ai/dsh-agent'
import type {} from '@deepseek-ai/dsh-llm'
import { SessionId } from '@deepseek-ai/dsh-session'
import { SessionId, type SessionHeader } from '@deepseek-ai/dsh-session'
import type { Session } from '@deepseek-ai/dsh-session'
import type {} from '@deepseek-ai/dsh-system-prompt'
import type {} from '@deepseek-ai/dsh-tools'
@@ -39,6 +39,8 @@ export interface Config {
agents: (AgentOptions & {
/** Agent id to register under; also seeds the fresh per-run session id (`${id}-session-<uuid>`). */
id: AgentId
/** Optional workspace cwd for the config-created fresh session. */
cwd?: string
/**
* If set, the config agent RESUMES this persisted session id instead of
* starting a fresh `${id}-session-<uuid>`. Sourced from an env var in
@@ -82,6 +84,7 @@ export class AgentLoop extends Service implements AgentFactory {
agents: z.array(z.object({
id: z.string().required(),
model: z.string(),
cwd: z.string(),
resumeSessionId: z.string(),
})).default([]),
}) as unknown as z<Config>
@@ -101,7 +104,7 @@ export class AgentLoop extends Service implements AgentFactory {
// (renderPrompt then rejects a persona that claims it — fail loud).
ctx.systemPrompt.variable('model', context => context.agent?.options.model)
ctx.systemPrompt.variable('cwd', context => context.agent?.session.header.cwd)
for (const { id, resumeSessionId, ...options } of config.agents) {
for (const { id, cwd, resumeSessionId, ...options } of config.agents) {
if (resumeSessionId !== undefined && resumeSessionId !== '') {
// Resume a prior session instead of starting fresh. resume() needs
// `ctx.sessionPersistence`, which may load AFTER this plugin (cordis.yml
@@ -120,15 +123,15 @@ export class AgentLoop extends Service implements AgentFactory {
return () => void fiber.dispose()
}, `agentLoop.resume(${id})`)
} else {
this.create(id, options)
this.create(id, options, cwd === undefined ? {} : { cwd })
}
}
}
/**
* Config-driven create: an agent on a FRESH, non-colliding session id per run
* (`${id}-session-<uuid>`, no cwd). Used for `cordis.yml`-configured agents
* and as the shared core for the programmatic factory {@link createAgent}.
* (`${id}-session-<uuid>`). Used for `cordis.yml`-configured agents and as
* the shared core for the programmatic factory {@link createAgent}.
*
* Why a per-run id, not a fixed `${id}-session`: once a durable persistence
* backend is loaded, a fixed id collides on the second run — the backend
@@ -142,15 +145,16 @@ export class AgentLoop extends Service implements AgentFactory {
* UI/ACP path owns session selection.
* @param id - the agent id; also seeds the generated session id.
* @param options - loop options (model, limits, …); defaults applied per option.
* @param meta - optional session metadata for the fresh session.
* @returns the running agent, owned by the calling fiber (no handle).
*/
create(id: AgentId, options: AgentOptions = {}): ReactLoopAgent {
create(id: AgentId, options: AgentOptions = {}, meta: Pick<SessionHeader, 'cwd'> = {}): ReactLoopAgent {
this.assertAgentIdFree(id)
// Config/programmatic path: prepare the session and let start() fold its
// lifecycle into the agent's composite effect (so a fiber unload tears the
// session + agent down as one ordered chain, capturing the loop's closing
// flush). The whole effect is owned by THIS fiber; no AgentHandle is needed.
const session = this.ctx.sessions.prepare(SessionId(`${id}-session-${randomUUID()}`), { meta: {} })
const session = this.ctx.sessions.prepare(SessionId(`${id}-session-${randomUUID()}`), { meta })
const { agent } = this.start(id, options, session, 'startup')
return agent
}