Zero
Общие порты, координация и компоненты с чистым API. Их можно использовать отдельно и связывать внешними инструментами композиции, без обязательного DI-контейнера в каждом модуле.
Схемы текущей реализации ниже — не будущий дизайн. Сначала процессы, затем все механизмы замены по уровню доступности. Зелёный — существующий параметр и реальный потребитель, не сертификат полной работоспособности. Выбор LLM и файлов настроек показан отдельно от DI.
Направление развития · планируемое разделение
Zero — headless/backendless harness: нейтральные контракты и независимо используемые модули без обязательной модели, оболочки или инфраструктуры поставщика. Backendless не означает отсутствие процессов, вычислений или хранения.
Общие порты, координация и компоненты с чистым API. Их можно использовать отдельно и связывать внешними инструментами композиции, без обязательного DI-контейнера в каждом модуле.
Конкретная референсная сборка: Pi Durable, native execution/storage и выбранные реализации модулей. Использует публичные контракты Zero; не становится обязательной начинкой всех других harness.
Терминал опционален и не является частью ядра. CLI, TUI, web и другие клиенты — внешние оболочки headless/backendless harness. Их отключение не должно останавливать runtime. Текущий native terminal — клиент Durable Zero, не требование к Zero.
Лучше всего руководствоваться репозиторием arch-rules и его полным скоупом AP-правил. Выбирайте применимые правила по границам изменения и риску; правила про БД, UI или распределённое исполнение применяйте там, где есть соответствующая ответственность.
Скиллы не обязательны. Способ работы и инструменты остаются на выбор контрибьютора. Важны архитектурные ограничения, реальные доказательства поведения и объяснение неприменимых правил, а не установка набора скиллов.
Core зависит от общих портов, не от Pi Durable, TUI, BB или конкретной БД. Native DTO не становятся общими простым переименованием. Необязательные адаптеры могут поставляться отдельными пакетами.
Конкретные реализации и defaults выбирает recipe/composition root. Независимым компонентам не нужен обязательный DI-SDK. Нет скрытого global container, monkey-patching или регистрации с побочными эффектами импорта.
Один владелец исполнения на открытую сессию; в Durable Zero это native Harness. Отключение клиента не равно shutdown. Определены creation, readiness, cancellation, cleanup и поведение reload при активной работе.
Фиксируйте версии и capabilities. Используйте публичные package exports, не соседний ../src и не приватные поля SDK. Unsupported — явная ошибка, а не фиктивный успех. Breaking changes требуют миграционного решения.
Сначала сквозной рабочий путь. Add, replace и remove — разные операции; конфигурация модели не равна замене движка. Переданная зависимость реально вызывается, стандартная замещённая реализация не исполняется. Admission receipt не является завершённым результатом.
Ниже — карта текущего main 7ef44a2. Целевые принципы выше и фактическая зрелость API ниже — разные слои. Цвет обозначает способ подключения, не сертификат полной работоспособности.
Реализуйте указанный контракт и подключите его через entry. Проверьте реального consumer и ограничения. Доступный параметр ещё не сертифицирует вашу комбинацию.
Нижний native seam существует, но публичный recipe его не пропускает. Сначала нужен явный путь конфигурации либо отдельная сборка.
Обоснуйте необходимость нового контракта или используйте свой host/recipe. Не обходите границу копированием внутренних файлов.
Это внутренняя ответственность конкретной Pi-backed сборки. Не требуется превращать каждый конструктор SDK в универсальный DI-слот.
Сначала подключите production-путь и покажите реальное исполнение. Наличие интерфейса или standalone helper не делает функцию готовой.
Для замены движка смотрите IAgentEngine, для окружения — Core execution environment / native factory, для добавления модулей — extensions. Новый слот сначала обсуждается как контракт, а не прячется в обходном пути.
Вход: assembleDurableZero({ engine })
Сейчас: IAgentEngine → PiDurableEngine по умолчанию → SessionCoordinator.engine.start / submit / abort / close
Как заменить: Передать другую реализацию IAgentEngine в основной API сборки.
Ограничение: Generic engine получает stdio/Core, но сборка не возвращает ему native view/controller; terminal host требует эти порты.
Проверка: Есть fast-path mock-engine тест; это не доказательство совместимости любого engine с native terminal.
if (options.engine) {return await this.engine.submit(input, options);...(nativeEngine ? { view: nativeEngine.view, controller: nativeEngine.controller, isIdle: () => nativeEngine!.isIdle() } : {}),if (!instance.view || !instance.controller) throw new Error("The terminal runtime requires native presentation and control ports");Вход: assembleDurableZero({ nativeExecutionEnvFactory })
Сейчас: SDK NodeExecutionEnv по умолчанию → ExecutionEnvs → Harness.env → filesystem / exec tools
Как заменить: Передать factory(target, chordContext), возвращающую SDK ExecutionEnv.
Ограничение: Нельзя одновременно передать Core factory. Полный SDK-контракт обязателен; CLI/socket-host не выставляют этот параметр.
Проверка: В native-env-seam.test.ts есть реальные startup/file-effect проверки. В этом разборе не перезапускались.
options.nativeExecutionEnvFactory ??const created = await this.#nativeFactory(target, context);Вход: assembleDurableZero({ executionEnvFactory })
Сейчас: IExecutionEnvironment → CoreExecutionEnvAdapter → ExecutionEnvs → native Harness.env
Как заменить: Передать свою Core factory; можно использовать adapter из пакета execution.
Ограничение: Core SPI уже native SDK; неподдержанные расширенные операции дают typed unsupported, это не полная замена любого native backend.
Проверка: В native-env-seam.test.ts есть native startup + effect через custom Core factory; здесь не перезапускались.
if (options.executionEnvFactory && options.nativeExecutionEnvFactory) {const createdCore = await this.#coreFactory({ cwd: key, conversationId: target.conversationId });adapter = new CoreExecutionEnvAdapter(createdCore, key);Вход: assembleDurableZero({ extensions, skills, noGlobalExtensions })
Сейчас: DefaultResourceLoader + ExtensionRunner → installExtensionTools → native Registry; pending providers → ModelRuntime
Как заменить: Передать пути extensions/skills; расширение регистрирует tools/providers через Pi API.
Ограничение: Это работающий путь добавления, но не API удаления/замены стандартных builtins или интегратора MCP.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
...(options.extensions ?? (options.extension ? [options.extension] : [])),await resourceLoader.reload();installExtensionTools(registry, adapted);Вход: assembleDurableZero({ stdin, stdout, onNotification })
Сейчас: Readable/Writable + callback → StdioJsonRpcServer и coordinator.events
Как заменить: Передать свои streams и слушатель уведомлений.
Ограничение: Заменяются IO/наблюдатель, не сама transport strategy; socket-host использует именно этот путь.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
...(options.stdin !== undefined ? { stdin: options.stdin } : {}),options.onNotification?.("session/event", payload);...options, sessionDir: resolve(options.sessionDir), noGlobalExtensions: true, stdin: input, stdout: output,Вход: runDurableTui(source, controller, settings, ...)
Сейчас: TerminalViewSource / TerminalController / TerminalSettings → terminal-ui/vendor/experimental/durable/tui.js
Как заменить: Своя оболочка может передать реализации трёх UI-контрактов в библиотечную функцию.
Ограничение: Это локальная DI на UI-границе. Native DurableView — намеренная concrete UI-зависимость, не утечка в Core.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
export function runDurableTui(source: TerminalViewSource, controller: TerminalController, settings: TerminalSettings,const selected = await runDurableTui(client, client, {Вход: PiDurableEngine.dependencies.modelRuntime
Сейчас: Pi ModelRuntime; fallback ModelRuntime.create() → Harness.open({ models: modelRuntime }); controller.setModel
Как заменить: Собрать PiDurableEngine/openDurable с dependencies.modelRuntime; верхний recipe придётся адаптировать.
Ограничение: assembleDurableZero не принимает этот слот. Требуется native ModelRuntime contract, не Core IModelProvider.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
readonly modelRuntime?: ModelRuntime;const modelRuntime = deps?.modelRuntime ?? (await ModelRuntime.create());{ models: modelRuntime, registry, settings, env: envs.env, onReport: report },Вход: PiDurableEngine.dependencies.nativeProviders
Сейчас: SDK Provider[] → modelRuntime.registerNativeProvider(provider)
Как заменить: Передать nativeProviders на нижнем уровне; через Pi extensions также есть registerProvider/native-provider путь.
Ограничение: provider/model строки выбирают модель, но не заменяют ModelRuntime. Прямой список providers не открыт верхним recipe.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
readonly nativeProviders?: readonly Provider[];for (const provider of deps?.nativeProviders ?? []) {try { modelRuntime.registerNativeProvider(provider); } catch (err) { logRegError("native provider", provider?.id ?? "unknown", err); }Вход: PiDurableEngine.dependencies.settingsManager
Сейчас: Native SettingsManager → createHarnessSettings; model defaults; resource loading
Как заменить: Передать совместимый native SettingsManager на нижнем уровне.
Ограничение: Верхний runner всегда создаёт SettingsManager.create(cwd). Это native тип, не произвольный zero-neutral config provider.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
const settingsManager = SettingsManager.create(cwd);const settingsManager = deps?.settingsManager ?? SettingsManager.create(location.cwd);Вход: PiDurableEngine.dependencies.resourceLoader
Сейчас: DefaultResourceLoader; extension-pi-resource оборачивает его → reload/getExtensions/getSkills → prompt и ExtensionRunner
Как заменить: Передать native-compatible loader при ручной сборке PiDurableEngine/openDurable.
Ограничение: Основная фабрика всегда создаёт DefaultResourceLoader. extension-pi-resource — data/wiring wrapper, а не отдельный универсальный resource backend.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
const resourceLoader = new DefaultResourceLoader({readonly resourceLoader?: DefaultResourceLoader;deps?.resourceLoader ??return Object.assign(baseExt, {Вход: dependencies.builtins / openDurable.builtins или plugins
Сейчас: BuiltinExtension[] (контракт в adapter-pi-durable) → createCodingRegistry → builtinRegistry.initAll(ctx)
Как заменить: Нижний API принимает дополнительные BuiltinExtension. Сам контракт содержит native Registry/SettingsManager.
Ограничение: Верхний runner передаёт только штатный codemode. На нижнем уровне список добавляется к coding-tools/subagents, не заменяет их.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
...(deps?.builtins ?? []),...(options.builtins ?? options.plugins ?? []),builtinRegistry.register(codingToolsExtension);registry?: Registry;Вход: dependencies.builtins: [свой codemode]
Сейчас: adapter-codemode → createCodemodeExtension → BuiltinExtension.register → registry.install(ext)
Как заменить: Через ручную нижнюю сборку выбрать другой codemode-builtin вместо пакета adapter-codemode.
Ограничение: Основной runner жёстко вызывает createCodemodeBuiltinExtension(); добавлять второй codemode поверх его списка нельзя.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
codemodeBuiltin = createCodemodeBuiltinExtension();builtins: [codemodeBuiltin],ctx.registry.install(ext);Вход: dependencies.prompt / openDurable.prompt
Сейчас: PromptOptions: systemPromptPath/contextFiles/... → createPiPrompt → buildSections
Как заменить: Нижний API принимает PromptOptions; paths/sections меняют содержимое.
Ограничение: Сам renderer createPiPrompt обязателен. resourceLoader/dynamicSections в итоговых options подставляются runtime-loader сверху.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
const promptOptions = deps?.prompt ?? options.prompt;{ ...promptOptions, resourceLoader, dynamicSections },const systemPromptOverride = tryReadPromptFile(options.systemPromptPath, "system-prompt");Вход: dependencies.dynamicSections
Сейчас: DynamicPromptSections по умолчанию → ExtensionRunner и createPiPrompt читают/обновляют sections
Как заменить: Передать совместимый объект на нижнем уровне.
Ограничение: Верхний assembleDurableZero не выставляет этот параметр.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
const dynamicSections = deps?.dynamicSections ?? new DynamicPromptSections();dynamicSections.updateSections(beforeStart.systemPromptOptions.sections);Вход: Нет factory/constructor option
Сейчас: new CodemodeSandbox() внутри register → createCodemodeExtension({ sandbox })
Как заменить: Сейчас — заменить весь codemode builtin нижним API либо менять adapter-codemode; независимого sandboxFactory нет.
Ограничение: Нельзя передать другой sandbox основному codemode адаптеру снаружи.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
const sandbox = new CodemodeSandbox();const ext = createCodemodeExtension({Вход: Всегда добавляется в createCodingRegistry
Сейчас: coding-tools.ts → SDK CodingTools/createCodingTools → native Registry → read/write/edit/bash tools
Как заменить: Для полной замены builtin нужен иной recipe/изменение bootstrap: выбрать финальный список до регистрации.
Ограничение: customBuiltins с тем же id отклоняется. Это не утверждение, что Pi extensions вообще не могут переопределить отдельное имя tool.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
builtinRegistry.register(codingToolsExtension);if (this.#extensions.has(extension.id)) {ctx.registry.install(tools);Вход: Всегда добавляется в createCodingRegistry
Сейчас: builtin-extensions/subagents.ts → native Registry → SDK subagent extension
Как заменить: Для замены/исключения целого builtin изменить конфигурационный bootstrap, не снимать защиту от неявных дублей.
Ограничение: Настройка policy — другое: меняет данные политики, не реализацию builtin.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
builtinRegistry.register(subagentsExtension);id: "subagents",Вход: runner extensionFactories: createMcpExtension()
Сейчас: Native Pi MCP ExtensionFactory → ResourceLoader → ExtensionRunner → adapted tools
Как заменить: Конфигурация серверов — Pi config; замена самого интегратора требует собственного resource loader/recipe.
Ограничение: Отдельного upper-level mcpFactory нет. В текущем main MCP status больше не является startup readiness barrier; readiness вызывается перед native tool snapshots, cleanup — через session_shutdown. Это не слот замены интегратора.
Проверка: Изменённый startup/tool path прослежен по исходникам main; runtime/MCP тесты при обновлении атласа не запускались.
extensionFactories: [{ name: "mcp", factory: createMcpExtension() }],for (const ready of mcpReadiness) await ready({Вход: Создаются/устанавливаются внутри Pi bootstrap
Сейчас: createToolSearchExtension + DynamicCodemodeNamespaces → native registry / extension tool sync
Как заменить: Отдельного внешнего factory option нет; для смены реализации менять Pi bootstrap или реализовывать свой backend.
Ограничение: Это internal Pi integration, не готовый generic Zero slot.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
registry.install(createToolSearchExtension());const dynamicNamespaces = new DynamicCodemodeNamespaces();Вход: Всегда registry.install(createPiPrompt(...))
Сейчас: adapter-pi-durable/engine/prompt.ts → native registry sections
Как заменить: Для другой реализации renderer нужен иной конфигурационный bootstrap.
Ограничение: PromptOptions позволяют менять данные, не выбирать другой renderer.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
registry.install(createPiPrompt(settingsManager, cwd, promptOptions));export function createPiPrompt(_settings: SettingsManager, fallbackCwd: string, options: PromptOptions = {}) {Вход: runner: new StdioJsonRpcServer(...)
Сейчас: protocol/StdioJsonRpcServer implements ITransportStrategy → Inline handler → SessionCoordinator
Как заменить: Streams доступны сверху, но transportFactory/ITransportStrategy в assemble options отсутствует. Для иной стратегии — собственный host/bootstrap.
Ограничение: Socket-host оборачивает stdio через PassThrough; это реальный другой host, а не injected transport strategy.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
const server = new StdioJsonRpcServer({export class StdioJsonRpcServer implements ITransportStrategy {const bridge = new StdioJsonRpcServer({Вход: local-terminal/main.ts: fixed dynamic import
Сейчас: @local-durable/terminal-ui.runDurableTui → ZeroTerminalClient через Native view/controller contract
Как заменить: Другой frontend может использовать RPC client и свои widgets, но main не принимает render/UI factory.
Ограничение: Терминал опционален и не является частью ядра headless/backendless Zero. В готовом terminal entrypoint выбор UI фиксирован; другой frontend использует свой entrypoint/клиент.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
const { runDurableTui } = await import("@local-durable/terminal-ui");const selected = await runDurableTui(client, client, {Вход: runtime-loader: openNodeSqliteStorage(location.database)
Сейчас: SDK SQLite storage → Harness.open → native sessions/tasks/documents
Как заменить: Никакого storageFactory в текущем Pi recipe нет. Для другого native storage нужен отдельный обоснованный adapter seam; другой backend возможен через engine SPI.
Ограничение: Это намеренно конкретный Pi-backed backend, не требование заменить SQLite сейчас. Core ISessionStore не управляет этим запуском.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
await openNodeSqliteStorage(location.database),harness = await Harness.open(Вход: PiDurableEngine → openDurable → Harness.open
Сейчас: @earendil-works/pi-durable + Chord → Model/tool/session execution; view/controller создаёт adapter
Как заменить: В Durable реализации сохраняется один native Harness. Для другого engine есть внешний IAgentEngine слот.
Ограничение: Отдельный HarnessFactory не выставлен. Не требуется инъектировать каждую SDK-внутренность или создавать второй execution loop.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
harness = await Harness.open(this.#instance = await openDurable({Вход: Есть в пакетах; в standard recipe не подключён
Сейчас: PiAiModelProvider; setupRunnerModels → Самостоятельный model-setup helper, не native runtime-loader
Как заменить: Не считать наличие Core порта рабочим способом смены моделей в штатном Harness; реальный нижний слот — native modelRuntime.
Ограничение: Не нужно механически заставлять native Harness работать через этот unused generic port.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
export interface IModelProvider {const modelProvider = new PiAiModelProvider(modelRuntime, settingsManager);const modelRuntime = deps?.modelRuntime ?? (await ModelRuntime.create());Вход: Есть standalone adapter; coordinator принимает только engine/sessionDir
Сейчас: PiDurableSessionStore implements ISessionStore → Не mounted в native reference startup
Как заменить: В этой сборке заменить store через Core нельзя: native SQLite открывается отдельно.
Ограничение: Не вводить параллельного владельца native session ради использования существующего интерфейса.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
export interface ISessionStore {export class PiDurableSessionStore implements ISessionStore {constructor(options: SessionCoordinatorOptions) {Вход: runner HostBridge(requester), Core IHostBridge; Pi dependencies.hostBridge — placeholder
Сейчас: HostBridge и legacy HostRpcManager существуют отдельно → Сборщик не создаёт HostBridge; dependencies.hostBridge в execution path не читается
Как заменить: Для рабочего BB integration нужен реальный mount native host tools и request/response routing; DI поле само ничего не подключает.
Ограничение: Core bridge и пустой Pi IHostBridge — разные типы. Legacy module globals не являются приемлемым DI решением.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
export class HostBridge implements IHostBridge {readonly hostBridge?: IHostBridge;let activeToolForwarder: ((toolName: string, args: unknown) => Promise<{ content: Array<{ type: "text"; text: string }>; isError: boolean }>) | null = null;Вход: CodemodeExtension implements ISandboxExtension
Сейчас: Lifecycle metadata id/name/version/teardown → Нет метода create/execute sandbox в этом Core SPI
Как заменить: Это не sandbox injection point; реальная замена sandbox требует factory в codemode adapter либо другого builtin.
Ограничение: Не принимать implements интерфейса за возможность подменить конкретный CodemodeSandbox.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
export interface ISandboxExtension {export class CodemodeExtension implements BuiltinExtension, ISandboxExtension {const sandbox = new CodemodeSandbox();Вход: createSessionDispatcher(options) существует
Сейчас: runner/dispatch/session-dispatcher.ts → В reference composition свой ограниченный inline handler
Как заменить: Не считать методы helper/schema автоматически mounted; production routing нужно подключать и проверять отдельно.
Ограничение: Замена resolver/container не реализует отсутствующие tools/register, binding/fork capabilities.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
export function createSessionDispatcher(options: SessionDispatcherOptions): MethodHandler {handler: async (method: string, params: Record<string, unknown>) => {Вход: Можно явно использовать его NodeExecutionEnv в Core factory
Сейчас: @durable-zero/execution vs native SDK NodeExecutionEnv → По умолчанию runner импортирует SDK env/node, не execution package
Как заменить: Пакет можно выбрать в своём executionEnvFactory; он не является default native execution backend.
Ограничение: Наличие workspace dependency в manifest не значит, что она включена в production recipe.
Проверка: Не запускался новый runtime-тест; оценка по исходникам.
import { NodeExecutionEnv } from "@earendil-works/pi-durable/env/node";export class LocalNodeExecutionEnv implements IExecutionEnvironment {Вход: DurableController.executeMcpCommand(args, ui)
Сейчас: Native MCP handler + предоставленный ExtensionUIContext → local-runtime MCP UI bridge → terminal RPC events
Как заменить: Передать custom/select/input/notify/theme через UI-контекст команды или использовать session/mcpCommand. Headless context всего runtime не заменяется.
Ограничение: Это native Durable controller boundary, не универсальная замена MCP-интегратора и не обязательный UI ядра. Один активный manager принадлежит конкретному соединению.
Проверка: Публичная сигнатура и реальный handler/consumer проверены по исходникам; native UI тесты не перезапускались.
executeMcpCommand(args: string, ui: Pick<ExtensionUIContext, "custom" | "select" | "input" | "notify" | "theme">): Promise<void>;await registered.handler(args, { ...context, ui: { ...context.ui, ...ui }, mode: "tui", hasUI: true });const ui = createMcpCommandUI(request,Вход: local-runtime attachAssembly → openSessionCatalog(...)
Сейчас: Filesystem SessionCatalog: list / describe / rename / create → session/list, session/describe, session/rename, session/create
Как заменить: Сейчас выбрать иной metadata catalog можно только своим host recipe / изменением bootstrap; catalogFactory не передаётся.
Ограничение: Это только host metadata: имена, cwd и набор extensions. Native entries/models/tasks/locks остаются у Harness; каталог не является вторым владельцем исполнения или session store.
Проверка: Конструктор, metadata-only граница и реальные RPC consumers прослежены по main; runtime тесты не запускались.
catalog = await openSessionCatalog(assemblyOptions.sessionDir!, assemblyOptions.cwd ?? process.cwd(), configuredExtensions);/** Host catalog metadata only. Native entries, models, tasks and locks remain in the Harness. */case "session/list": return catalog.list();По C4/arc42: статические зависимости, сборочная конфигурация и runtime-сообщения — разные виды. Нельзя читать стрелку импорта как поток исполнения. Type-only и manifest-only зависимости не рисуются как активное runtime-подключение.
Область охвата: Все workspace packages + публичные assembly options, PiRuntimeDependencies, builtin/resource/model/storage/host/transport/UI seams. Не все транзитивные npm packages и не каждый private helper SDK.