Merge branch 'codex/simp-agent-entry-state' into codex/simp-unify-agent-session-id

# Conflicts:
#	docs/cordis-catalog/events.md
#	docs/cordis-catalog/services.md
#	docs/event-producer-consumer.md
#	docs/module-graph.md
#	packages/bash/tool-bash/src/index.ts
#	packages/ui/stdio-agent/README.md
#	packages/ui/stdio-agent/src/index.ts
#	packages/ui/stdio/src/index.ts
#	packages/workflow/workflow-workerthread/tests/workflow-workerthread.spec.ts
#	packages/workflow/workflow/package.json
#	scripts/gen-doc-graphs.ts
This commit is contained in:
Tianyi Cui
2026-07-15 16:24:43 +08:00
305 changed files with 8816 additions and 2144 deletions

View File

@@ -0,0 +1,19 @@
import { describe, expect, it } from 'vitest'
import Loader from '@cordisjs/plugin-loader'
import * as stdio from '../src/index.ts'
/** Real Loader export-path guard for the namespace stdio plugin. */
describe('dsh-stdio plugin export shape', () => {
it('preserves name, inject, Config, and apply through Loader unwrapping', () => {
expect('default' in stdio).toBe(false)
expect(typeof stdio.apply).toBe('function')
const loader = Object.create(Loader.prototype) as Loader
const unwrapped = loader.unwrapExports(stdio) as Record<string, unknown>
expect(unwrapped).toBe(stdio)
expect(unwrapped.name).toBe('ui-stdio')
expect(unwrapped.inject).toEqual(['agents', 'userInteraction'])
expect(unwrapped.Config).toBeDefined()
expect(typeof unwrapped.apply).toBe('function')
})
})

View File

@@ -0,0 +1,54 @@
import { EventEmitter } from 'node:events'
import type { Readable, Writable } from 'node:stream'
import { describe, expect, it, vi } from 'vitest'
import type { Context } from 'cordis'
import type { StdioRuntime } from '../src/index.ts'
const createInterface = vi.hoisted(() => vi.fn(() => {
const reader = new EventEmitter() as EventEmitter & { close(): void }
reader.close = vi.fn()
return reader
}))
vi.mock('node:readline', () => ({ createInterface }))
function fakeContext(): Context {
return {
on: vi.fn(() => vi.fn()),
effect: vi.fn((callback: () => () => void) => callback()),
// The UI seeds its root target from the registry at install; this suite only
// exercises readline terminal-mode selection, so an empty roster suffices.
agents: { roots: vi.fn(() => []) },
userInteraction: { registerProvider: vi.fn(() => vi.fn()) },
} as unknown as Context
}
function fakeRuntime(inputIsTTY: boolean, outputIsTTY: boolean): StdioRuntime {
return {
input: { isTTY: inputIsTTY } as Readable & { isTTY: boolean },
output: { isTTY: outputIsTTY, write: vi.fn(() => true) } as unknown as Writable & { isTTY: boolean },
exit: vi.fn(),
}
}
describe('createStdioChat readline mode', () => {
it('enables terminal editing only when both stdio streams are TTYs', async () => {
const { createStdioChat } = await import('../src/index.ts')
const tty = fakeRuntime(true, true)
createStdioChat(fakeContext(), {}, tty)
expect(createInterface).toHaveBeenLastCalledWith({
input: tty.input,
output: tty.output,
terminal: true,
})
const piped = fakeRuntime(true, false)
createStdioChat(fakeContext(), {}, piped)
expect(createInterface).toHaveBeenLastCalledWith({
input: piped.input,
output: piped.output,
terminal: false,
})
})
})

File diff suppressed because it is too large Load Diff