index.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. /**
  2. * Model-facing read, write, and edit tools over `ctx.fs`. This package owns schemas, validation,
  3. * read windows, formatting, and observation events, never a concrete provider. An optional
  4. * event policy supplies mutation guards; without one the tools use unconditional provider calls.
  5. * @module @deepseek-ai/dsh-tool-fs
  6. */
  7. import type { Context } from 'cordis'
  8. import z from 'schemastery'
  9. import { applyReadTool, READ_LIMIT, STREAM_MIN_SIZE } from './read.ts'
  10. import { applyWriteTool } from './write.ts'
  11. import { applyEditTool } from './edit.ts'
  12. import { READ_MAX_BYTES, READ_MAX_LINE_LENGTH } from './read-render.ts'
  13. /** Cordis plugin name used by loader diagnostics. */
  14. export const name = 'tool-fs'
  15. /** Services required by the filesystem tool suite. */
  16. export const inject = ['tools', 'fs', 'systemPrompt']
  17. /** Plugin config (all optional — `Config` supplies the defaults). */
  18. export interface Config {
  19. /** Default and maximum number of lines returned by one `read` call. */
  20. readLimit?: number
  21. /** Maximum characters returned for a single line before truncation. */
  22. readMaxLineLength?: number
  23. /** Maximum bytes returned for the selected lines of one `read` call. */
  24. readMaxBytes?: number
  25. /** Files at or above this size stream instead of loading whole into memory. */
  26. readStreamMinSize?: number
  27. }
  28. export const Config: z<Config> = z.object({
  29. readLimit: z.number().default(READ_LIMIT),
  30. readMaxLineLength: z.number().default(READ_MAX_LINE_LENGTH),
  31. readMaxBytes: z.number().default(READ_MAX_BYTES),
  32. readStreamMinSize: z.number().default(STREAM_MIN_SIZE),
  33. })
  34. /** The shape after schemastery applied the defaults. */
  35. type ResolvedConfig = Required<Config>
  36. /** Every read cap counts lines/chars/bytes — a positive integer, or windowing arithmetic misbehaves silently. */
  37. function assertPositiveInteger(name: string, value: number): void {
  38. if (!Number.isInteger(value) || value < 1) {
  39. throw new Error(`tool-fs: ${name} must be a positive integer`)
  40. }
  41. }
  42. /** Register the full `read`/`write`/`edit` filesystem tool suite. */
  43. export function apply(ctx: Context, config: Config): void {
  44. // schemastery (Config) has already filled every defaulted field.
  45. const resolved = config as ResolvedConfig
  46. assertPositiveInteger('readLimit', resolved.readLimit)
  47. assertPositiveInteger('readMaxLineLength', resolved.readMaxLineLength)
  48. assertPositiveInteger('readMaxBytes', resolved.readMaxBytes)
  49. assertPositiveInteger('readStreamMinSize', resolved.readStreamMinSize)
  50. applyReadTool(ctx, {
  51. limit: resolved.readLimit,
  52. maxLineLength: resolved.readMaxLineLength,
  53. maxBytes: resolved.readMaxBytes,
  54. streamMinSize: resolved.readStreamMinSize,
  55. })
  56. applyWriteTool(ctx)
  57. applyEditTool(ctx)
  58. }