index.ts 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /**
  2. * Service Definition for the code-execution capability seam that runs one model-written program against host async bindings.
  3. * Runtimes know nothing about tools or sessions; consumers own those concerns.
  4. * @module @deepseek-ai/dsh-code-runtime
  5. */
  6. import { Context, Service } from '@deepseek-ai/cordis'
  7. import type { CodeRunRequest, CodeRunResult } from './types.ts'
  8. export type {
  9. CodeBindingErrorClass,
  10. CodeBindingFunction,
  11. CodeBindingNamespace,
  12. CodeJsonValue,
  13. CodeRunFailure,
  14. CodeRunRequest,
  15. CodeRunResult,
  16. } from './types.ts'
  17. /**
  18. * Binding globals EVERY backend refuses because SOME backend owns the slot in
  19. * the program's namespace: `console` (the worker's log capture), and
  20. * `__dsh_main__`/`__builtins__`/`__name__` (the Python backend's bootstrap
  21. * wrapper and seeded module globals),
  22. * and `__debug__`. One shared set — rather than each backend refusing only its
  23. * own slots — keeps the portability promise real: a namespace list valid on
  24. * one backend is valid on all, so a caller cannot pick a name that works on
  25. * the worker and collides on Python (or vice versa). `__name__` et al. ARE
  26. * valid portable identifiers, so the identifier rule on
  27. * `CodeBindingNamespace.global` never rejects them — hence this explicit set.
  28. * (Error members differ: {@link DUNDER_MEMBER} refuses every dunder form
  29. * wholesale; binding globals refuse only the names listed here.) `__debug__`
  30. * is listed for a different reason than a collision: CPython compiles a bare
  31. * `__debug__` reference to the constant `True` and rejects any assignment to
  32. * the name at COMPILE time, so an injected global under that name is
  33. * unreachable from the program — accepted by validation, unusable on the
  34. * Python backend, which is exactly the split the shared set exists to prevent.
  35. */
  36. export const RESERVED_BINDING_GLOBALS: ReadonlySet<string> = new Set([
  37. 'console',
  38. '__dsh_main__', '__builtins__', '__name__', '__debug__',
  39. ])
  40. /**
  41. * `CodeBindingErrorClass.memberNameProperty` names EVERY backend refuses, as
  42. * one shared contract so a request valid on one backend is valid on all. The
  43. * JS `Error` exclusions (`name`, `message`, `stack`) and Python's
  44. * exception-protocol members (`args`, `with_traceback`, `add_note`) are
  45. * listed by name; dunder-form names (`__x__`, non-empty middle) are refused
  46. * wholesale — several are constrained CPython descriptors whose `setattr`
  47. * raises while constructing the rejection, and the exact set is an interpreter
  48. * version detail. Any other non-empty own property name is accepted everywhere.
  49. */
  50. export const RESERVED_ERROR_MEMBERS: ReadonlySet<string> = new Set([
  51. 'name', 'message', 'stack',
  52. 'args', 'with_traceback', 'add_note',
  53. ])
  54. /**
  55. * Dunder form (`__x__`, non-empty middle): object-protocol slots in Python,
  56. * refused as {@link RESERVED_ERROR_MEMBERS | error members} on every backend.
  57. */
  58. export const DUNDER_MEMBER = /^__.+__$/
  59. /**
  60. * Reserved words of every portable target language (ECMAScript ∪ Python),
  61. * refused as {@link CodeBindingNamespace.global} / error-class names by all
  62. * backends, one per language: the released TypeScript worker thread and the
  63. * experimental, private CPython subprocess. The portable-identifier contract
  64. * promises a namespace list valid on one backend is valid on every backend; a
  65. * per-language check would let `lambda` pass the TypeScript backend and fail
  66. * the Python one. Extending the seam with a new language means widening this
  67. * union (a breaking review of existing binding names, by design).
  68. */
  69. export const PORTABLE_RESERVED_WORDS: ReadonlySet<string> = new Set([
  70. // ECMAScript reserved words and reserved-in-strict-mode names.
  71. 'await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do',
  72. 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'import', 'in',
  73. 'instanceof', 'new', 'null', 'return', 'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof',
  74. 'var', 'void', 'while', 'with', 'yield', 'let', 'static', 'implements', 'interface', 'package',
  75. 'private', 'protected', 'public', 'arguments', 'eval',
  76. // Python 3.x keywords and soft keywords not already above ('type' and '_'
  77. // are soft keywords: legal names in practice, reserved here for safety).
  78. 'False', 'None', 'True', 'and', 'as', 'assert', 'async', 'def', 'del', 'elif', 'except', 'from',
  79. 'global', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'match', 'type', '_',
  80. ])
  81. declare module '@deepseek-ai/cordis' {
  82. interface Context {
  83. codeRuntime: CodeRuntime
  84. }
  85. }
  86. /**
  87. * Registers one `ctx.codeRuntime` implementation. Program, budget, abort, and substrate
  88. * failures resolve in {@link CodeRunResult}; only Service Definition contract misuse rejects. Implementations bridge
  89. * structured-cloneable bindings, materialize each declared namespace rejection
  90. * class, treat programs as hostile peers, isolate runs from one another, and
  91. * terminate and await in-flight runs during disposal.
  92. */
  93. export abstract class CodeRuntime extends Service {
  94. /**
  95. * The source language {@link run} expects `program` to be written in, as a
  96. * lowercase identifier. Informational, not gating — a consumer that
  97. * generates language-specific presentation (typed SDK stubs, usage
  98. * instructions) switches on it and fails loud on a language it cannot
  99. * present. Well-known values: `'typescript'` and `'python'`, those
  100. * `dsh-tools` presents; the TypeScript backend is released, the Python
  101. * backend is experimental and private (not published).
  102. */
  103. abstract readonly language: string
  104. /**
  105. * The execution substrate, as a lowercase identifier. Informational, not
  106. * gating — a descriptor so deployments and diagnostics can tell backends
  107. * apart, not a security claim. Well-known values: `'worker-thread'`,
  108. * `'process'`, `'container'`.
  109. */
  110. abstract readonly isolation: string
  111. constructor(ctx: Context) {
  112. super(ctx, 'codeRuntime')
  113. }
  114. /**
  115. * Execute one program against the request's bindings and capture what it
  116. * emitted. See the class doc for the resolution contract (error is a result
  117. * field; rejection means Service Definition contract misuse only).
  118. * @param request - the program, its bindings, and the abort signal; the
  119. * request carries everything the runtime acts on, with no hidden defaults.
  120. * @returns the run's outcome: completion value (when transferable), the
  121. * ordered log capture, and the failure (if any).
  122. */
  123. abstract run(request: CodeRunRequest): Promise<CodeRunResult>
  124. }
  125. export default CodeRuntime