index.ts 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /**
  2. * Model-facing, workspace-authorized session-history search and read tools.
  3. *
  4. * @module @deepseek-ai/dsh-tool-session-query
  5. */
  6. import type { Context } from 'cordis'
  7. import z from 'schemastery'
  8. import { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout'
  9. import { defineTool } from '@deepseek-ai/dsh-tools'
  10. import type {} from '@deepseek-ai/dsh-system-prompt'
  11. import { toolInput } from './input.ts'
  12. import { operations } from './operations.ts'
  13. import { presentation } from './presentation.ts'
  14. /** Cordis plugin name used by Loader diagnostics. */
  15. export const name = 'tool-session-query'
  16. /** Capability services required by the model-facing consumer. */
  17. export const inject = ['tools', 'systemPrompt', 'sessionQuery']
  18. /** Default maximum number of authorized search hits returned by one call. */
  19. export const DEFAULT_MAX_SEARCH_RESULTS = 100
  20. /** Default cooperative deadline for either full-text search tool. */
  21. export const DEFAULT_SEARCH_TIMEOUT_MS = 30_000
  22. /** Deployment-owned search count and timeout bounds. */
  23. export interface Config {
  24. /** Maximum authorized hits returned by one search call. Defaults to 100. */
  25. maxSearchResults?: number
  26. /** Cooperative full-text search deadline in milliseconds. Defaults to 30000. */
  27. searchTimeoutMs?: number
  28. }
  29. /** Schemastery config for Loader defaults and generated configuration docs. */
  30. export const Config: z<Config> = z.object({
  31. maxSearchResults: z.number().step(1).min(1).default(DEFAULT_MAX_SEARCH_RESULTS),
  32. searchTimeoutMs: z.number().step(1).min(1).max(MAX_TIMER_DELAY_MS).default(DEFAULT_SEARCH_TIMEOUT_MS),
  33. })
  34. interface ResolvedConfig {
  35. readonly maxSearchResults: number
  36. readonly searchTimeoutMs: number
  37. }
  38. const TEXT_OUTPUT = {
  39. schema: { type: 'string' as const },
  40. render: (_args: unknown, value: string) => [{ type: 'text' as const, text: value }],
  41. }
  42. const PROMPT_TEXT =
  43. 'Use session_search to find relevant work from prior sessions, or session_event_search to search earlier '
  44. + 'events in one session. Search results are cursor-free and workspace-scoped. Follow a useful hit with '
  45. + 'session_trace, session_event_trace, or session_event_read when you need lineage, relationships, or exact data.'
  46. /** Register all five tools and their shared model guidance. */
  47. export function apply(ctx: Context, config: Config): void {
  48. const resolved = resolveConfig(config)
  49. ctx.systemPrompt.section({
  50. name: 'tool:session-query',
  51. order: 113,
  52. text: PROMPT_TEXT,
  53. })
  54. ctx.tools.register(defineTool({
  55. name: 'session_search',
  56. description: 'Search prior sessions in the caller workspace and return the strongest matching event from each session.',
  57. parameters: toolInput.sessionSearchParameters,
  58. output: TEXT_OUTPUT,
  59. timeoutMs: resolved.searchTimeoutMs,
  60. execute: (args, exec) => operations.executeSessionSearch(ctx, args, exec, resolved.maxSearchResults),
  61. presentCall: presentation.presentSessionSearchCall,
  62. }))
  63. ctx.tools.register(defineTool({
  64. name: 'session_event_search',
  65. description: 'Search prior events in one authorized session; the current session excludes the step performing this call.',
  66. parameters: toolInput.eventSearchParameters,
  67. output: TEXT_OUTPUT,
  68. timeoutMs: resolved.searchTimeoutMs,
  69. execute: (args, exec) => operations.executeEventSearch(ctx, args, exec, resolved.maxSearchResults),
  70. presentCall: presentation.presentEventSearchCall,
  71. }))
  72. ctx.tools.register(defineTool({
  73. name: 'session_trace',
  74. description: 'Read the authorized session lineage around one session, including complete visible ancestor and descendant relationships.',
  75. parameters: toolInput.targetSessionParameter,
  76. output: TEXT_OUTPUT,
  77. isConcurrencySafe: () => true,
  78. execute: (args, exec) => operations.executeSessionTrace(ctx, args, exec),
  79. presentCall: presentation.presentSessionTraceCall,
  80. }))
  81. ctx.tools.register(defineTool({
  82. name: 'session_event_trace',
  83. description: 'Read every direct replacement and provenance relationship for one event in an authorized session.',
  84. parameters: {
  85. ...toolInput.targetSessionParameter,
  86. seq: { type: 'integer', required: true, description: 'Target event sequence number.' },
  87. },
  88. output: TEXT_OUTPUT,
  89. isConcurrencySafe: () => true,
  90. execute: (args, exec) => operations.executeEventTrace(ctx, args, exec),
  91. presentCall: args => presentation.presentEventTargetCall('Trace event', args),
  92. }))
  93. ctx.tools.register(defineTool({
  94. name: 'session_event_read',
  95. description: 'Read one full unabridged event and optional neighboring raw-event summaries from an authorized session.',
  96. parameters: {
  97. ...toolInput.targetSessionParameter,
  98. seq: { type: 'integer', required: true, description: 'Target event sequence number.' },
  99. before: { type: 'integer', description: 'Number of preceding raw events to summarize. Omit for none.' },
  100. after: { type: 'integer', description: 'Number of following raw events to summarize. Omit for none.' },
  101. },
  102. output: TEXT_OUTPUT,
  103. isConcurrencySafe: () => true,
  104. execute: (args, exec) => operations.executeEventRead(ctx, args, exec),
  105. presentCall: args => presentation.presentEventTargetCall('Read event', args),
  106. }))
  107. }
  108. function resolveConfig(config: Config): ResolvedConfig {
  109. const maxSearchResults = config.maxSearchResults ?? DEFAULT_MAX_SEARCH_RESULTS
  110. const searchTimeoutMs = config.searchTimeoutMs ?? DEFAULT_SEARCH_TIMEOUT_MS
  111. if (!Number.isSafeInteger(maxSearchResults) || maxSearchResults < 1) {
  112. throw new TypeError('tool-session-query: maxSearchResults must be a positive safe integer')
  113. }
  114. if (!Number.isInteger(searchTimeoutMs) || searchTimeoutMs < 1 || searchTimeoutMs > MAX_TIMER_DELAY_MS) {
  115. throw new TypeError(
  116. `tool-session-query: searchTimeoutMs must be a positive integer no greater than ${MAX_TIMER_DELAY_MS}`,
  117. )
  118. }
  119. return { maxSearchResults, searchTimeoutMs }
  120. }