index.ts 6.5 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; see the [portable-identifier Agent
  22. * Note](../../../../.agents/notes/implemented/architecture/2026-07-31-code-runtime-portable-identifier-seam.md)),
  23. * and `__debug__`. One shared set — rather than each backend refusing only its
  24. * own slots — keeps the portability promise real: a namespace list valid on
  25. * one backend is valid on all, so a caller cannot pick a name that works on
  26. * the worker and collides on Python (or vice versa). `__name__` et al. ARE
  27. * valid portable identifiers, so the identifier rule on
  28. * `CodeBindingNamespace.global` never rejects them — hence this explicit set.
  29. * (Error members differ: {@link DUNDER_MEMBER} refuses every dunder form
  30. * wholesale; binding globals refuse only the names listed here.) `__debug__`
  31. * is listed for a different reason than a collision: CPython compiles a bare
  32. * `__debug__` reference to the constant `True` and rejects any assignment to
  33. * the name at COMPILE time, so an injected global under that name is
  34. * unreachable from the program — accepted by validation, unusable on the
  35. * Python backend, which is exactly the split the shared set exists to prevent.
  36. */
  37. export const RESERVED_BINDING_GLOBALS: ReadonlySet<string> = new Set([
  38. 'console',
  39. '__dsh_main__', '__builtins__', '__name__', '__debug__',
  40. ])
  41. /**
  42. * `CodeBindingErrorClass.memberNameProperty` names EVERY backend refuses, as
  43. * one shared contract so a request valid on one backend is valid on all. The
  44. * JS `Error` exclusions (`name`, `message`, `stack`) and Python's
  45. * exception-protocol members (`args`, `with_traceback`, `add_note`) are
  46. * listed by name; dunder-form names (`__x__`, non-empty middle) are refused
  47. * wholesale — several are constrained CPython descriptors whose `setattr`
  48. * raises while constructing the rejection, and the exact set is an interpreter
  49. * version detail. Any other non-empty own property name is accepted everywhere.
  50. */
  51. export const RESERVED_ERROR_MEMBERS: ReadonlySet<string> = new Set([
  52. 'name', 'message', 'stack',
  53. 'args', 'with_traceback', 'add_note',
  54. ])
  55. /**
  56. * Dunder form (`__x__`, non-empty middle): object-protocol slots in Python,
  57. * refused as {@link RESERVED_ERROR_MEMBERS | error members} on every backend.
  58. */
  59. export const DUNDER_MEMBER = /^__.+__$/
  60. /**
  61. * Reserved words of every portable target language (ECMAScript ∪ Python),
  62. * refused as {@link CodeBindingNamespace.global} / error-class names by all
  63. * backends. Python is a portability target here even though only the
  64. * TypeScript worker has a published backend. The portable-identifier contract
  65. * promises a namespace list valid on one backend is valid on every backend; a
  66. * per-language check would let `lambda` pass the TypeScript backend and fail
  67. * the Python one. Extending the seam with a new language means widening this
  68. * union (a breaking review of existing binding names, by design).
  69. */
  70. export const PORTABLE_RESERVED_WORDS: ReadonlySet<string> = new Set([
  71. // ECMAScript reserved words and reserved-in-strict-mode names.
  72. 'await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do',
  73. 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'import', 'in',
  74. 'instanceof', 'new', 'null', 'return', 'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof',
  75. 'var', 'void', 'while', 'with', 'yield', 'let', 'static', 'implements', 'interface', 'package',
  76. 'private', 'protected', 'public', 'arguments', 'eval',
  77. // Python 3.x keywords and soft keywords not already above ('type' and '_'
  78. // are soft keywords: legal names in practice, reserved here for safety).
  79. 'False', 'None', 'True', 'and', 'as', 'assert', 'async', 'def', 'del', 'elif', 'except', 'from',
  80. 'global', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'match', 'type', '_',
  81. ])
  82. declare module '@deepseek-ai/cordis' {
  83. interface Context {
  84. codeRuntime: CodeRuntime
  85. }
  86. }
  87. /**
  88. * Registers one `ctx.codeRuntime` implementation. Program, budget, abort, and substrate
  89. * failures resolve in {@link CodeRunResult}; only Service Definition contract misuse rejects. Implementations bridge
  90. * structured-cloneable bindings, materialize each declared namespace rejection
  91. * class, treat programs as hostile peers, isolate runs from one another, and
  92. * terminate and await in-flight runs during disposal.
  93. */
  94. export abstract class CodeRuntime extends Service {
  95. /**
  96. * The source language {@link run} expects `program` to be written in, as a
  97. * lowercase identifier. Informational, not gating — a consumer that
  98. * generates language-specific presentation (typed SDK stubs, usage
  99. * instructions) switches on it and fails loud on a language it cannot
  100. * present. Well-known values: `'typescript'` and `'python'`, those
  101. * `dsh-tools` presents; only `'typescript'` has a published backend.
  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