boot.ts 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. /**
  2. * Core spine composition for the dsh host: mounts the harness core plugins
  3. * one by one (each awaited so a load failure surfaces deterministically at
  4. * boot, unlike bundle plugins whose children mount unawaited).
  5. */
  6. import { Context } from 'cordis'
  7. import Timer from '@cordisjs/plugin-timer'
  8. import LlmService from '@deepseek-ai/dsh-llm'
  9. import SessionStore from '@deepseek-ai/dsh-session'
  10. import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
  11. import ToolRegistry from '@deepseek-ai/dsh-tools'
  12. import AgentRegistry from '@deepseek-ai/dsh-agent'
  13. import TaskService from '@deepseek-ai/dsh-tasks'
  14. import AgentLoop from '@deepseek-ai/dsh-agent-loop'
  15. import * as LlmDeepSeek from '@deepseek-ai/dsh-llm-deepseek'
  16. import SessionPersistenceJsonl from '@deepseek-ai/dsh-session-persistence-jsonl'
  17. import LocalBashExecutor from '@deepseek-ai/dsh-bash-local'
  18. import * as toolBash from '@deepseek-ai/dsh-tool-bash'
  19. import * as toolTodo from '@deepseek-ai/dsh-tool-todo'
  20. import * as toolTasks from '@deepseek-ai/dsh-tool-tasks'
  21. import FsLocal from '@deepseek-ai/dsh-fs-local'
  22. import * as fsPolicy from '@deepseek-ai/dsh-fs-policy'
  23. import * as toolFs from '@deepseek-ai/dsh-tool-fs'
  24. import * as toolFsSearch from '@deepseek-ai/dsh-tool-fs-search'
  25. import * as workspaceContext from '@deepseek-ai/dsh-workspace-context'
  26. import SkillService from '@deepseek-ai/dsh-skill'
  27. import * as SkillLocal from '@deepseek-ai/dsh-skill-local'
  28. import * as toolSkill from '@deepseek-ai/dsh-tool-skill'
  29. import TokenMeter from '@deepseek-ai/dsh-token-meter'
  30. import CompactBasic from '@deepseek-ai/dsh-compact-basic'
  31. import SubagentService from '@deepseek-ai/dsh-subagent'
  32. import * as SubagentSpawn from '@deepseek-ai/dsh-subagent-spawn'
  33. import * as SubagentFork from '@deepseek-ai/dsh-subagent-fork'
  34. import * as toolSubagent from '@deepseek-ai/dsh-tool-subagent'
  35. import WorkflowWorkerthread from '@deepseek-ai/dsh-workflow-workerthread'
  36. import * as toolWorkflow from '@deepseek-ai/dsh-tool-workflow'
  37. import * as timeoutPolicy from '@deepseek-ai/dsh-timeout-policy'
  38. import SpillLocal from '@deepseek-ai/dsh-spill-local'
  39. import * as spillPolicy from '@deepseek-ai/dsh-spill-policy'
  40. import UserInteractionService from '@deepseek-ai/dsh-user-interaction'
  41. /** Options for bootHost — the assembly-layer composition knobs. */
  42. export interface BootHostOptions {
  43. /** Root directory for JSONL session persistence. */
  44. persistenceRoot: string
  45. /** Workspace-instruction byte budget/config, or false to disable AGENTS.md/CLAUDE.md loading. */
  46. workspaceContext: workspaceContext.Config | false
  47. /** Default provider route for created/resumed agents (defaults to 'deepseek', the only adapter bootHost registers). */
  48. provider?: string
  49. /** Default model id (defaults to 'deepseek-v4-flash', matching the demos). */
  50. model?: string
  51. /**
  52. * Default project directory for sessions created without an explicit cwd
  53. * (defaults to the host process working directory). A session's cwd is its
  54. * project path — a per-session choice, not a host property; this option only
  55. * supplies the value used when the creator does not choose one.
  56. */
  57. cwd?: string
  58. }
  59. /** Host-level default agent routing: the single source injected on create and reported by host.describe. */
  60. export interface HostDefaults {
  61. provider: string
  62. model: string
  63. /** Default project directory for new sessions whose create request carries no cwd. */
  64. cwd: string
  65. }
  66. /** Booted host handle: composed root context + resolved defaults + disposer. */
  67. export interface HostHandle {
  68. /** Root context with the full plugin assembly mounted. */
  69. ctx: Context
  70. /** Resolved default agent routing (options ?? built-in fallbacks). */
  71. defaults: HostDefaults
  72. /** Tear down the whole plugin tree. */
  73. dispose(): Promise<void>
  74. }
  75. /**
  76. * Compose the harness host plugin assembly (the one place deciding which plugins mount and
  77. * with what defaults — shells must not alter the assembly).
  78. * @param options - persistence, workspace instructions, and optional default routing.
  79. * @returns the booted handle (ctx + defaults + dispose).
  80. */
  81. export async function bootHost(options: BootHostOptions): Promise<HostHandle> {
  82. const defaults: HostDefaults = {
  83. provider: options.provider ?? 'deepseek',
  84. model: options.model ?? 'deepseek-v4-flash',
  85. cwd: options.cwd ?? process.cwd(),
  86. }
  87. const ctx = new Context()
  88. await ctx.plugin(Timer)
  89. await ctx.plugin(LlmService)
  90. await ctx.plugin(SessionStore)
  91. await ctx.plugin(SystemPrompt, { persona: '' })
  92. await ctx.plugin(ToolRegistry)
  93. await ctx.plugin(UserInteractionService)
  94. await ctx.plugin(AgentRegistry)
  95. await ctx.plugin(TaskService)
  96. await ctx.plugin(AgentLoop, { agents: [] })
  97. await ctx.plugin(LlmDeepSeek, {})
  98. await ctx.plugin(SessionPersistenceJsonl, { root: options.persistenceRoot })
  99. await ctx.plugin(LocalBashExecutor, {})
  100. // Tool suite mirroring the demo:repl composition (repl-agent/cordis.yml +
  101. // the agent-spine bundle) so web sessions get the same coding-agent tool
  102. // face; deviations are noted inline.
  103. await ctx.plugin(toolBash, {})
  104. await ctx.plugin(toolTodo)
  105. await ctx.plugin(toolTasks, {})
  106. // fs paths resolve against the host default project rather than the raw
  107. // process cwd — the same source create() injects into session.cwd.
  108. await ctx.plugin(FsLocal, { cwd: defaults.cwd })
  109. await ctx.plugin(fsPolicy)
  110. await ctx.plugin(toolFs, {})
  111. await ctx.plugin(toolFsSearch, {})
  112. if (options.workspaceContext !== false) {
  113. await ctx.plugin(workspaceContext, options.workspaceContext)
  114. }
  115. // Skill stack with the demo default dshHome (~/.dsh via resolveDshHome).
  116. await ctx.plugin(SkillService, {})
  117. await ctx.plugin(SkillLocal, {})
  118. await ctx.plugin(toolSkill, {})
  119. // Request pressure + compaction (service-wide defaults, as in repl-agent).
  120. await ctx.plugin(TokenMeter)
  121. await ctx.plugin(CompactBasic)
  122. // Subagent spawn/fork backends and their two model-facing tool instances.
  123. await ctx.plugin(SubagentService)
  124. await ctx.plugin(SubagentSpawn, { providerName: 'spawn' })
  125. await ctx.plugin(SubagentFork, { providerName: 'fork' })
  126. await ctx.plugin(toolSubagent, { provider: 'spawn', toolName: 'subagent' })
  127. await ctx.plugin(toolSubagent, { provider: 'fork', toolName: 'subagent_fork' })
  128. await ctx.plugin(WorkflowWorkerthread, { provider: 'spawn' })
  129. await ctx.plugin(toolWorkflow, {})
  130. // Declared per-tool timeouts become enforced deadlines.
  131. await ctx.plugin(timeoutPolicy)
  132. // Oversized tool output spills to session-scoped files (repl-agent budget).
  133. await ctx.plugin(SpillLocal, {})
  134. await ctx.plugin(spillPolicy, { maxInlineBytes: 50000 })
  135. return { ctx, defaults, dispose: () => ctx.fiber.dispose() }
  136. }