Files
deepseek-harness/packages/self-modification/tool-cordis/tests/helpers.ts
imccyu ec601ca13d build(vendor): rescope the vendored Cordis packages into @deepseek-ai
Machine-produced by `pnpm run rescope-vendor --apply` plus the regeneration it
prints: `pnpm install` for the lockfile, `pnpm run gen-third-party-notices`,
`verify-translation-pairing --write` for the touched bilingual pairs,
`gen-doc-graphs`, and one typert snapshot whose ids embed character offsets.
`pnpm run rescope-vendor --check` verifies the result.

Renames nine vendored packages (cordis, cosmokit, schemastery and the six
@cordisjs plugins) and every reference that resolves them: manifest names and
dependency keys, module specifiers including declare-module merges, cordis.yml
plugin names, tsconfig paths, every Markdown fence, and `docs/` prose.
Directory names, upstream versions, and dependency ranges are unchanged, so
vendor/README.md still reads as an upstream snapshot; its manifest table gains
an upstream-name column so THIRD_PARTY_NOTICES keeps MIT attribution pointed
at each fork's origin.

The tutorial tier follows the rename end to end: its yaml fences named plugins
the Loader can no longer resolve, its `ts ignore-check` fences disagreed with
the compiled fences beside them, and its prose quoted both. The contracts that
told readers to keep upstream names — the root convention and the vendoring
cookbook's tree comment and manifest invariant — now say to rescope instead.

Two rules read `@deepseek-ai/` as "another workspace plugin": the client bundle
purity gate now names the vendored libraries a browser bundle inlines, and the
files where a bare `cordis` is an agent-preset id keep that product data.
2026-08-10 22:04:13 +08:00

127 lines
4.0 KiB
TypeScript

import { Context } from '@deepseek-ai/cordis'
import Timer from '@deepseek-ai/cordis-plugin-timer'
import { CallId } from '@deepseek-ai/dsh-llm'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry from '@deepseek-ai/dsh-tools'
import type { ToolDefinition, ToolExecutionResult } from '@deepseek-ai/dsh-tools'
import * as tool from '../src/index.ts'
const testToolSignal = new AbortController().signal
/**
* Shared spec helpers: a real `SystemPrompt` + `ToolRegistry` + timer +
* tool-cordis tree (only the model is absent — the code strings below stand in
* for what it would write), plus the canonical mount-code fixtures the suites
* share.
*/
/** Mount the plugin on a fresh context with a real ToolRegistry and the timer service. */
export async function setup(config?: tool.Config): Promise<Context> {
const ctx = new Context()
await ctx.plugin(Timer)
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(tool, config)
return ctx
}
let callCounter = 0
/** Execute a registered tool through the real registry pipeline. */
export function call(ctx: Context, name: string, args: unknown): Promise<ToolExecutionResult> {
return ctx.tools.execute({ signal: testToolSignal, callId: CallId(`call-${++callCounter}`), name, arguments: args })
}
/** Concatenated text blocks of one tool result. */
export function text(result: ToolExecutionResult): string {
return result.content.filter(block => block.type === 'text').map(block => block.text).join('')
}
/** Mount code for a listener plugin: logs on every `tools/change`. */
export const LISTENER_CODE = `
return {
name: 'change-logger',
apply(ctx) {
ctx.on('tools/change', () => console.log('tools changed'))
},
}
`
/** Explicit content-array output declaration for dynamic-tool behavior fixtures. */
export const CONTENT_OUTPUT_CODE = `
output: {
schema: { type: 'array', items: { type: 'json' } },
render(_args, value) { return value },
},`
/** Mount code for a self-made tool: registers `reverse_text` via the sandbox's harness helpers. */
export const REVERSE_TOOL_CODE = `
return {
name: 'reverse-text',
inject: ['tools'],
apply(ctx) {
harness.registerTool(ctx, harness.defineTool({
name: 'reverse_text',
description: 'Reverse a string.',
parameters: { text: { type: 'string', required: true } },
output: {
schema: { type: 'string' },
render(_args, value) {
return [{ type: 'text', text: value }]
},
},
async execute(args) {
return args.text.split('').reverse().join('')
},
}))
},
}
`
/** Mount code providing a `greeter` service other mounts can inject. */
export const PROVIDER_CODE = `
return {
name: 'greeter-provider',
apply(ctx) {
ctx.provide('greeter', { greet: (name) => 'hi ' + name })
},
}
`
/** Mount code consuming the `greeter` service through inject, exposing it as a tool. */
export const CONSUMER_CODE = `
return {
name: 'greeter-consumer',
inject: ['greeter', 'tools'],
apply(ctx) {
harness.registerTool(ctx, harness.defineTool({
name: 'greet',
description: 'Greet someone via the greeter service.',
parameters: { name: { type: 'string', required: true } },
output: {
schema: { type: 'string' },
render(_args, value) {
return [{ type: 'text', text: value }]
},
},
async execute(args) {
return ctx.greeter.greet(args.name)
},
}))
},
}
`
/** A registrable no-op tool the tests use to trigger a real `tools/change`. */
export function dummyTool(name: string): ToolDefinition {
return {
name,
description: 'test trigger',
parameters: { type: 'object' as const, properties: {} },
output: { schema: { type: 'null' }, render: () => [] },
async execute(): Promise<null> {
return null
},
}
}