138 lines
5.8 KiB
TypeScript
138 lines
5.8 KiB
TypeScript
/**
|
|
* Model-facing, workspace-authorized session-history search and read tools.
|
|
*
|
|
* @module @deepseek-ai/dsh-tool-session-query
|
|
*/
|
|
|
|
import type { Context } from 'cordis'
|
|
import z from 'schemastery'
|
|
import { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout'
|
|
import { defineTool } from '@deepseek-ai/dsh-tools'
|
|
import type {} from '@deepseek-ai/dsh-system-prompt'
|
|
import { toolInput } from './input.ts'
|
|
import { operations } from './operations.ts'
|
|
import { presentation } from './presentation.ts'
|
|
|
|
/** Cordis plugin name used by Loader diagnostics. */
|
|
export const name = 'tool-session-query'
|
|
|
|
/** Capability services required by the model-facing consumer. */
|
|
export const inject = ['tools', 'systemPrompt', 'sessionQuery']
|
|
|
|
/** Default maximum number of authorized search hits returned by one call. */
|
|
export const DEFAULT_MAX_SEARCH_RESULTS = 100
|
|
|
|
/** Default cooperative deadline for either full-text search tool. */
|
|
export const DEFAULT_SEARCH_TIMEOUT_MS = 30_000
|
|
|
|
/** Deployment-owned search count and timeout bounds. */
|
|
export interface Config {
|
|
/** Maximum authorized hits returned by one search call. Defaults to 100. */
|
|
maxSearchResults?: number
|
|
/** Cooperative full-text search deadline in milliseconds. Defaults to 30000. */
|
|
searchTimeoutMs?: number
|
|
}
|
|
|
|
/** Schemastery config for Loader defaults and generated configuration docs. */
|
|
export const Config: z<Config> = z.object({
|
|
maxSearchResults: z.number().step(1).min(1).default(DEFAULT_MAX_SEARCH_RESULTS),
|
|
searchTimeoutMs: z.number().step(1).min(1).max(MAX_TIMER_DELAY_MS).default(DEFAULT_SEARCH_TIMEOUT_MS),
|
|
})
|
|
|
|
interface ResolvedConfig {
|
|
readonly maxSearchResults: number
|
|
readonly searchTimeoutMs: number
|
|
}
|
|
|
|
const TEXT_OUTPUT = {
|
|
schema: { type: 'string' as const },
|
|
render: (_args: unknown, value: string) => [{ type: 'text' as const, text: value }],
|
|
}
|
|
|
|
const PROMPT_TEXT =
|
|
'Use session_search to find relevant work from prior sessions, or session_event_search to search earlier '
|
|
+ 'events in one session. Search results are cursor-free and workspace-scoped. Follow a useful hit with '
|
|
+ 'session_trace, session_event_trace, or session_event_read when you need lineage, relationships, or exact data.'
|
|
|
|
/** Register all five tools and their shared model guidance. */
|
|
export function apply(ctx: Context, config: Config): void {
|
|
const resolved = resolveConfig(config)
|
|
ctx.systemPrompt.section({
|
|
name: 'tool:session-query',
|
|
order: 113,
|
|
text: PROMPT_TEXT,
|
|
})
|
|
|
|
ctx.tools.register(defineTool({
|
|
name: 'session_search',
|
|
description: 'Search prior sessions in the caller workspace and return the strongest matching event from each session.',
|
|
parameters: toolInput.sessionSearchParameters,
|
|
output: TEXT_OUTPUT,
|
|
timeoutMs: resolved.searchTimeoutMs,
|
|
execute: (args, exec) => operations.executeSessionSearch(ctx, args, exec, resolved.maxSearchResults),
|
|
presentCall: presentation.presentSessionSearchCall,
|
|
}))
|
|
|
|
ctx.tools.register(defineTool({
|
|
name: 'session_event_search',
|
|
description: 'Search prior events in one authorized session; the current session excludes the step performing this call.',
|
|
parameters: toolInput.eventSearchParameters,
|
|
output: TEXT_OUTPUT,
|
|
timeoutMs: resolved.searchTimeoutMs,
|
|
execute: (args, exec) => operations.executeEventSearch(ctx, args, exec, resolved.maxSearchResults),
|
|
presentCall: presentation.presentEventSearchCall,
|
|
}))
|
|
|
|
ctx.tools.register(defineTool({
|
|
name: 'session_trace',
|
|
description: 'Read the authorized session lineage around one session, including complete visible ancestor and descendant relationships.',
|
|
parameters: toolInput.targetSessionParameter,
|
|
output: TEXT_OUTPUT,
|
|
isConcurrencySafe: () => true,
|
|
execute: (args, exec) => operations.executeSessionTrace(ctx, args, exec),
|
|
presentCall: presentation.presentSessionTraceCall,
|
|
}))
|
|
|
|
ctx.tools.register(defineTool({
|
|
name: 'session_event_trace',
|
|
description: 'Read every direct replacement and provenance relationship for one event in an authorized session.',
|
|
parameters: {
|
|
...toolInput.targetSessionParameter,
|
|
seq: { type: 'integer', required: true, description: 'Target event sequence number.' },
|
|
},
|
|
output: TEXT_OUTPUT,
|
|
isConcurrencySafe: () => true,
|
|
execute: (args, exec) => operations.executeEventTrace(ctx, args, exec),
|
|
presentCall: args => presentation.presentEventTargetCall('Trace event', args),
|
|
}))
|
|
|
|
ctx.tools.register(defineTool({
|
|
name: 'session_event_read',
|
|
description: 'Read one full unabridged event and optional neighboring raw-event summaries from an authorized session.',
|
|
parameters: {
|
|
...toolInput.targetSessionParameter,
|
|
seq: { type: 'integer', required: true, description: 'Target event sequence number.' },
|
|
before: { type: 'integer', description: 'Number of preceding raw events to summarize. Omit for none.' },
|
|
after: { type: 'integer', description: 'Number of following raw events to summarize. Omit for none.' },
|
|
},
|
|
output: TEXT_OUTPUT,
|
|
isConcurrencySafe: () => true,
|
|
execute: (args, exec) => operations.executeEventRead(ctx, args, exec),
|
|
presentCall: args => presentation.presentEventTargetCall('Read event', args),
|
|
}))
|
|
}
|
|
|
|
function resolveConfig(config: Config): ResolvedConfig {
|
|
const maxSearchResults = config.maxSearchResults ?? DEFAULT_MAX_SEARCH_RESULTS
|
|
const searchTimeoutMs = config.searchTimeoutMs ?? DEFAULT_SEARCH_TIMEOUT_MS
|
|
if (!Number.isSafeInteger(maxSearchResults) || maxSearchResults < 1) {
|
|
throw new TypeError('tool-session-query: maxSearchResults must be a positive safe integer')
|
|
}
|
|
if (!Number.isInteger(searchTimeoutMs) || searchTimeoutMs < 1 || searchTimeoutMs > MAX_TIMER_DELAY_MS) {
|
|
throw new TypeError(
|
|
`tool-session-query: searchTimeoutMs must be a positive integer no greater than ${MAX_TIMER_DELAY_MS}`,
|
|
)
|
|
}
|
|
return { maxSearchResults, searchTimeoutMs }
|
|
}
|