The two shipped surfaces offered different tools for no recorded reason: session checkpoints, tool-result pruning, the goal tools, and Ralph were in `tui.cordis.yml`; `tool-todo` and web search were in `web.cordis.yml`. Neither offered session search, a string-replacement editor, or a repeat-tool guard, though none of the three is surface-specific. Move the rows that are not surface-specific into `base.cordis.yml` and add those three. Web search moves there too — the TUI decision the change that made it a Web default deferred. Both surfaces now assemble the same 27 tools. This adds only. No row is removed from either surface and no existing row's configuration is edited: executors, sandbox composition, access defaults, `tools.mode`, and the workflow tool are exactly what they were. Two rows stay surface-specific: `tmux-context` (no terminal multiplexer in a browser) and `session-reference` (its index has one writer owner). Ship `dsh-mcp-client` as a dependency without a row: the plugin mounts one server per instance and `command` is required, so a default would name a third-party server and spawn it outside `ctx.bash` on every launch. The CLI README carries the YAML for mounting one from a personal config.
52 lines
2.1 KiB
TypeScript
52 lines
2.1 KiB
TypeScript
import type { Context } from 'cordis'
|
|
import type {
|
|
GenerateOptions,
|
|
LlmModelInfo,
|
|
LlmResolvedModelInfo,
|
|
StreamChunk,
|
|
} from '@deepseek-ai/dsh-llm'
|
|
import { LlmAdapter } from '@deepseek-ai/dsh-llm'
|
|
|
|
/** Terminal marker the preset smoke waits for before it asks the TUI to exit. */
|
|
export const COMPOSITION_REPLY_TEXT = 'Shipped composition acknowledged.'
|
|
|
|
// Provider id and model the keyless tail routes `main` to; that overlay is the
|
|
// only caller, so the pair lives here as plain constants.
|
|
const COMPOSITION_PROVIDER = 'composition-keyless'
|
|
const COMPOSITION_MODEL = 'composition-keyless-model'
|
|
|
|
/**
|
|
* Network-free adapter for the shipped-composition smoke. It answers every
|
|
* request — tool-ful agent turns and the tool-less auxiliary calls alike — with
|
|
* one fixed text and never calls a tool, because the assertion under test is the
|
|
* assembled tool catalog the loop logs, not any tool's behavior.
|
|
*/
|
|
class CompositionEchoAdapter extends LlmAdapter {
|
|
override listModels(provider: string): Promise<readonly LlmModelInfo[]> {
|
|
return Promise.resolve([{ provider, id: COMPOSITION_MODEL, name: 'Preset Keyless' }])
|
|
}
|
|
|
|
override resolveModel(provider: string, model: string): Promise<LlmResolvedModelInfo> {
|
|
return Promise.resolve({ provider, id: model, name: 'Preset Keyless', context: { contextWindow: 128_000 } })
|
|
}
|
|
|
|
override async * stream(_options: GenerateOptions): AsyncIterable<StreamChunk> {
|
|
yield { type: 'block-start', index: 0, blockType: 'text' }
|
|
for (const char of COMPOSITION_REPLY_TEXT) yield { type: 'text-delta', index: 0, text: char }
|
|
yield { type: 'block-end', index: 0, block: { type: 'text', text: COMPOSITION_REPLY_TEXT } }
|
|
yield { type: 'usage', usage: { inputTokens: 20, outputTokens: COMPOSITION_REPLY_TEXT.length } }
|
|
yield { type: 'finish', reason: { kind: 'stop' } }
|
|
}
|
|
}
|
|
|
|
export const name = 'composition-echo-llm'
|
|
export const inject = ['llm']
|
|
|
|
/**
|
|
* Register the network-free adapter the shipped-composition smoke routes through.
|
|
* @param ctx - the loader-mounted plugin context.
|
|
*/
|
|
export function apply(ctx: Context): void {
|
|
ctx.llm.registerAdapter([COMPOSITION_PROVIDER], new CompositionEchoAdapter())
|
|
}
|