Merge branch 'master' into refactor/agent-leaf-dependencies

This commit is contained in:
imccyu
2026-07-15 13:09:33 +08:00
committed by GitHub
12 changed files with 18 additions and 79 deletions

View File

@@ -387,18 +387,6 @@ export const EVENT_API: readonly EventApiEntry[] = [
signature: '\'session/flush\'(this: Scoped<Session>, session: Session): Promise<void> | void',
summary: 'Awaited parallel durability checkpoint: every listener runs and the caller awaits all of them, with no waterfall veto.',
},
{
name: 'skill/provider-added',
mode: 'emit',
signature: '\'skill/provider-added\'(provider: SkillProvider): void',
summary: 'A skill provider became resolvable in the `ctx.skills` registry.',
},
{
name: 'skill/provider-removed',
mode: 'emit',
signature: '\'skill/provider-removed\'(name: string): void',
summary: 'A skill provider left the registry because its plugin fiber was disposed.',
},
{
name: 'subagent/end',
mode: 'emit',

View File

@@ -119,23 +119,6 @@ declare module 'cordis' {
interface Context {
skills: SkillService
}
interface Events {
/**
* A skill provider became resolvable in the `ctx.skills` registry.
* Consumers can observe this instead of depending on Cordis plugin load
* order, which is concurrent for sibling plugins.
* @param provider - the provider that just registered.
* @mode emit
*/
'skill/provider-added'(provider: SkillProvider): void
/**
* A skill provider left the registry because its plugin fiber was disposed.
* @param name - the registry name that no longer resolves.
* @mode emit
*/
'skill/provider-removed'(name: string): void
}
}
interface IndexedCandidate {
@@ -191,19 +174,16 @@ export class SkillService extends Service {
throw new Error(`a skill provider named "${name}" is already registered`)
}
const providers = this.providers
const ctx = this.ctx
const order = this.nextProviderOrder
const invalidateCache = (): void => { this.invalidateCache() }
this.nextProviderOrder += 1
const dispose = ctx.effect(function* () {
const dispose = this.ctx.effect(function* () {
providers.set(name, { provider, order })
invalidateCache()
yield () => {
providers.delete(name)
invalidateCache()
ctx.emit('skill/provider-removed', name)
}
ctx.emit('skill/provider-added', provider)
}, 'skills.registerProvider()')
// eslint-disable-next-line @typescript-eslint/no-misused-promises -- synchronous cleanup; direct return preserves disposer identity
return dispose

View File

@@ -2,6 +2,8 @@
This package implements `WorkflowService` with one Node worker thread per run. The worker executes the orchestration script; child agents remain on the host and are reached through `ctx.subagents` over a typed host/worker protocol.
The package root exports the default engine plugin and its `Config`; the worker protocol, runtime, and session modules stay private to the implementation. The operational `./worker` entry remains the engine's spawn target.
The split has one primary purpose: a synchronous script loop cannot block the harness event loop, and a script that ignores cancellation can be terminated with its worker. It is not a security sandbox.
## Trust and isolation boundary

View File

@@ -18,11 +18,7 @@ import { validateMeta } from './meta.ts'
import type { WorkerInit, WorkerLimits } from './types.ts'
export { validateMeta } from './meta.ts'
export { HostToWorkerType, WorkerToHostType } from './protocol.ts'
export type { HostToWorkerMessage, HostToWorkerPayloads, WorkerToHostMessage, WorkerToHostPayloads } from './protocol.ts'
export { materializeFromRealm, MaterializeError } from './realm.ts'
export { WorkflowExecution, type ExecutionObserver } from './runtime.ts'
export { requireParentPort, runWorkerSession } from './session.ts'
export type {
ChildHandle,
ChildPort,
@@ -83,7 +79,7 @@ function assertBodyParses(body: string, name: string): void {
* `result` never rejects; the `workflow/*` events fire around the run per
* the seam contract.
*/
export class WorkerWorkflowEngine extends WorkflowService {
class WorkerWorkflowEngine extends WorkflowService {
static inject = ['subagents']
static Config: z<Config> = z.object({

View File

@@ -9,7 +9,8 @@ import SubagentService from '@deepseek-ai/dsh-subagent'
import type { SubagentCapabilities, SubagentProvider, SubagentResult, SubagentRun, SubagentStartRequest } from '@deepseek-ai/dsh-subagent'
import type { WorkflowMeta, WorkflowResult, WorkflowResultInfo, WorkflowRunInfo } from '@deepseek-ai/dsh-workflow'
import * as workerEngineModule from '../src/index.ts'
import WorkerWorkflowEngine, { HostToWorkerType, WorkerToHostType, type Config } from '../src/index.ts'
import WorkerWorkflowEngine, { type Config } from '../src/index.ts'
import { HostToWorkerType, WorkerToHostType } from '../src/protocol.ts'
/** A minimal parent stand-in: the engine only threads it through to the provider. */
function fakeParent(): Agent {
@@ -1326,6 +1327,7 @@ describe('dsh-workflow-workerthread', () => {
it('has the class-plugin export shape (default = the engine service class)', () => {
expect(workerEngineModule.default).toBe(WorkerWorkflowEngine)
expect('WorkerWorkflowEngine' in workerEngineModule).toBe(false)
const loader = Object.create(Loader.prototype) as Loader
const unwrapped: unknown = loader.unwrapExports(workerEngineModule)
expect(unwrapped).toBe(WorkerWorkflowEngine)