refactor(consumers): remove cross-package runtime relays

This commit is contained in:
imccyu 2026-08-30 02:29:52 +08:00
parent f4e49ccf8f
commit 9135a13a8b
167 changed files with 561 additions and 493 deletions

View file

@ -10,7 +10,6 @@ import { SessionId } from '@deepseek-ai/dsh-session'
import type { Agent } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent'
import type { PatchOptions } from '@deepseek-ai/cordis-plugin-include' import type { PatchOptions } from '@deepseek-ai/cordis-plugin-include'
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest' import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
import { settingsNamespace } from '@deepseek-ai/dsh-settings'
import { SUBAGENT_MODEL_SELECTION_SETTINGS_NAMESPACE } from '@deepseek-ai/dsh-tool-subagent/model-selection-settings' import { SUBAGENT_MODEL_SELECTION_SETTINGS_NAMESPACE } from '@deepseek-ai/dsh-tool-subagent/model-selection-settings'
import { SETTINGS_NAMESPACE, SHIPPED_PRESET_ROOT } from '@deepseek-ai/dsh-agent-presets' import { SETTINGS_NAMESPACE, SHIPPED_PRESET_ROOT } from '@deepseek-ai/dsh-agent-presets'
import { applyChildComposition, childSessionMeta } from '@deepseek-ai/dsh-subagent' import { applyChildComposition, childSessionMeta } from '@deepseek-ai/dsh-subagent'
@ -864,7 +863,7 @@ describe('the default preset as a user setting', () => {
it('composes an unnamed session from the stored default, not the composed one', async () => { it('composes an unnamed session from the stored default, not the composed one', async () => {
expect(ctx.agentPresets.defaultId).toBe('standard') expect(ctx.agentPresets.defaultId).toBe('standard')
await ctx.settings.update(settingsNamespace(SETTINGS_NAMESPACE), { default: 'minimal' }) await ctx.settings.update(SETTINGS_NAMESPACE, { default: 'minimal' })
try { try {
expect(ctx.agentPresets.defaultId).toBe('minimal') expect(ctx.agentPresets.defaultId).toBe('minimal')
@ -883,7 +882,7 @@ describe('the default preset as a user setting', () => {
// The context is shared with the rest of the file. `replace({})` drops // The context is shared with the rest of the file. `replace({})` drops
// the user section wholesale so the field re-inherits the composition // the user section wholesale so the field re-inherits the composition
// base; `update` merges, and would leave the override standing. // base; `update` merges, and would leave the override standing.
await ctx.settings.replace(settingsNamespace(SETTINGS_NAMESPACE), {}) await ctx.settings.replace(SETTINGS_NAMESPACE, {})
} }
expect(ctx.agentPresets.defaultId).toBe('standard') expect(ctx.agentPresets.defaultId).toBe('standard')

View file

@ -9,7 +9,6 @@ import { join } from 'node:path'
import type { Browser, Page } from 'playwright' import type { Browser, Page } from 'playwright'
import { chromium } from 'playwright' import { chromium } from 'playwright'
import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest' import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
import { settingsNamespace } from '@deepseek-ai/dsh-settings'
import { import {
assertFixtureInventory, captureStableAria, compareOrRefreshGolden, assertFixtureInventory, captureStableAria, compareOrRefreshGolden,
launchWebScaffold, watchConsole, webSnapshotMode, type WebScaffold, launchWebScaffold, watchConsole, webSnapshotMode, type WebScaffold,
@ -34,7 +33,7 @@ describe.skipIf(MODE === 'record')('web e2e: declared reasoning efforts reach th
// = the wire spelling dispatch would send (`max: ultra` renames; the // = the wire spelling dispatch would send (`max: ultra` renames; the
// valueless `off` means "supported, send nothing"). The route sets no // valueless `off` means "supported, send nothing"). The route sets no
// deployment default, so the pane leads with the provider-default entry. // deployment default, so the pane leads with the provider-default entry.
await scaffold.ctx.settings.update(settingsNamespace('llm-pi-ai'), { await scaffold.ctx.settings.update('llm-pi-ai', {
providers: { providers: {
'acme-gateway': { 'acme-gateway': {
displayName: 'Acme Gateway', displayName: 'Acme Gateway',

View file

@ -17,7 +17,6 @@ import type { Browser, Page } from 'playwright'
import { chromium } from 'playwright' import { chromium } from 'playwright'
import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest' import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
import { SessionId } from '@deepseek-ai/dsh-session' import { SessionId } from '@deepseek-ai/dsh-session'
import { settingsNamespace } from '@deepseek-ai/dsh-settings'
import { launchWebScaffold, watchConsole, type WebScaffold } from './scaffold.ts' import { launchWebScaffold, watchConsole, type WebScaffold } from './scaffold.ts'
import { ZH_BROWSER_LOCALE, connectFreshWorkspaceZh, saveFailureShot } from './support.ts' import { ZH_BROWSER_LOCALE, connectFreshWorkspaceZh, saveFailureShot } from './support.ts'
@ -62,7 +61,7 @@ describe('web e2e: the composer model switch is the default for later sessions',
// Declared through the settings seam rather than the Models page: this // Declared through the settings seam rather than the Models page: this
// scenario is about the composer, and the declaring flow is covered by // scenario is about the composer, and the declaring flow is covered by
// models-settings.e2e. // models-settings.e2e.
await scaffold.ctx.settings.update(settingsNamespace('llm-pi-ai'), { await scaffold.ctx.settings.update('llm-pi-ai', {
providers: { providers: {
[START_ROUTE]: { [START_ROUTE]: {
displayName: 'Origin Gateway', displayName: 'Origin Gateway',
@ -136,7 +135,7 @@ describe('web e2e: the composer model switch is the default for later sessions',
// default still names the route, and nothing serves it any more. // default still names the route, and nothing serves it any more.
// `replace`, not `update`: a merge patch of `{providers: {}}` leaves every // `replace`, not `update`: a merge patch of `{providers: {}}` leaves every
// stored profile in place. // stored profile in place.
await scaffold.ctx.settings.replace(settingsNamespace('llm-pi-ai'), { providers: {} }) await scaffold.ctx.settings.replace('llm-pi-ai', { providers: {} })
await expect.poll(async () => box.isEnabled(), { timeout: 15_000 }).toBe(false) await expect.poll(async () => box.isEnabled(), { timeout: 15_000 }).toBe(false)
expect(await box.getAttribute('data-placeholder')).toBe('当前模型不可用,请先选择模型') expect(await box.getAttribute('data-placeholder')).toBe('当前模型不可用,请先选择模型')

View file

@ -9,7 +9,6 @@ import { join } from 'node:path'
import type { Browser, Page } from 'playwright' import type { Browser, Page } from 'playwright'
import { chromium } from 'playwright' import { chromium } from 'playwright'
import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest' import { afterAll, beforeAll, describe, expect, it, onTestFailed } from 'vitest'
import { settingsNamespace } from '@deepseek-ai/dsh-settings'
import { import {
acknowledgeReloadConnectionLoss, assertFixtureInventory, captureStableAria, compareOrRefreshGolden, acknowledgeReloadConnectionLoss, assertFixtureInventory, captureStableAria, compareOrRefreshGolden,
launchWebScaffold, watchConsole, webSnapshotMode, type WebScaffold, launchWebScaffold, watchConsole, webSnapshotMode, type WebScaffold,
@ -118,7 +117,7 @@ describe.skipIf(MODE === 'record')('web e2e: first-run DeepSeek credential setup
// An old acknowledgement means materially revised copy: welcome returns, // An old acknowledgement means materially revised copy: welcome returns,
// while the already-configured provider step remains complete. // while the already-configured provider step remains complete.
await scaffold.ctx.settings.mutate(settingsNamespace(WELCOME_NOTICE_SETTINGS_NAMESPACE), [{ await scaffold.ctx.settings.mutate(WELCOME_NOTICE_SETTINGS_NAMESPACE, [{
op: 'set', path: [WELCOME_NOTICE_ACK_FIELD], value: 'previous-copy-version', op: 'set', path: [WELCOME_NOTICE_ACK_FIELD], value: 'previous-copy-version',
}]) }])
const thirdReloadWarnings = tripwire.warnings.length const thirdReloadWarnings = tripwire.warnings.length

View file

@ -57,7 +57,6 @@ import {
type Profile, type Profile,
} from '@deepseek-ai/dsh-app-boot' } from '@deepseek-ai/dsh-app-boot'
import { dshHomePath } from '@deepseek-ai/dsh-home-paths' import { dshHomePath } from '@deepseek-ai/dsh-home-paths'
import { settingsNamespace } from '@deepseek-ai/dsh-settings'
import { LlmAdapter } from '@deepseek-ai/dsh-llm' import { LlmAdapter } from '@deepseek-ai/dsh-llm'
import type { import type {
LlmModelInfo, LlmProviderInfo, LlmResolvedModelInfo, RetryPolicyConfig, StreamChunk, LlmModelInfo, LlmProviderInfo, LlmResolvedModelInfo, RetryPolicyConfig, StreamChunk,
@ -636,7 +635,7 @@ export async function launchWebScaffold(options: LaunchOptions = {}): Promise<We
await ctx.loader.await() await ctx.loader.await()
assertEntriesLoaded(ctx, 'web e2e scaffold') assertEntriesLoaded(ctx, 'web e2e scaffold')
if (options.welcomeNoticePending !== true) { if (options.welcomeNoticePending !== true) {
await ctx.settings.mutate(settingsNamespace(WELCOME_NOTICE_SETTINGS_NAMESPACE), [{ await ctx.settings.mutate(WELCOME_NOTICE_SETTINGS_NAMESPACE, [{
op: 'set', path: [WELCOME_NOTICE_ACK_FIELD], value: WELCOME_NOTICE_VERSION, op: 'set', path: [WELCOME_NOTICE_ACK_FIELD], value: WELCOME_NOTICE_VERSION,
}]) }])
} }

View file

@ -10,7 +10,6 @@ import { afterEach, expect, it } from 'vitest'
import { ToolCallId } from '@deepseek-ai/dsh-llm' import { ToolCallId } from '@deepseek-ai/dsh-llm'
import { canonicalPath, writableRoots } from '@deepseek-ai/dsh-sandbox' import { canonicalPath, writableRoots } from '@deepseek-ai/dsh-sandbox'
import { SessionId } from '@deepseek-ai/dsh-session' import { SessionId } from '@deepseek-ai/dsh-session'
import { settingsNamespace } from '@deepseek-ai/dsh-settings'
// Empty type imports carry the tools/sandboxPolicy/approval Context merges. // Empty type imports carry the tools/sandboxPolicy/approval Context merges.
import type {} from '@deepseek-ai/dsh-tools' import type {} from '@deepseek-ai/dsh-tools'
import type {} from '@deepseek-ai/dsh-sandbox-policy' import type {} from '@deepseek-ai/dsh-sandbox-policy'
@ -101,7 +100,7 @@ it('assembles the shipped Web transport, catalog, guidance, and defaults', async
], ],
} }
`) `)
await ctx.settings.update(settingsNamespace('llm-deepseek'), { await ctx.settings.update('llm-deepseek', {
retryPolicy: { mode: 'always', maxRetries: 5 }, retryPolicy: { mode: 'always', maxRetries: 5 },
}) })
expect(ctx.llm.providerRetryPolicy('deepseek-official')).toMatchInlineSnapshot(` expect(ctx.llm.providerRetryPolicy('deepseek-official')).toMatchInlineSnapshot(`
@ -112,7 +111,7 @@ it('assembles the shipped Web transport, catalog, guidance, and defaults', async
"mode": "always", "mode": "always",
} }
`) `)
await ctx.settings.update(settingsNamespace('llm-pi-ai'), { await ctx.settings.update('llm-pi-ai', {
providers: { providers: {
openai: {}, openai: {},
anthropic: { retryPolicy: { mode: 'always' } }, anthropic: { retryPolicy: { mode: 'always' } },

View file

@ -16,6 +16,7 @@ import { realpath } from 'node:fs/promises'
import { isAbsolute, resolve } from 'node:path' import { isAbsolute, resolve } from 'node:path'
import { Readable, Writable } from 'node:stream' import { Readable, Writable } from 'node:stream'
import Schema from '@deepseek-ai/schemastery' import Schema from '@deepseek-ai/schemastery'
import { brandString } from '@deepseek-ai/dsh-brand'
import { errorChain } from '@deepseek-ai/dsh-llm' import { errorChain } from '@deepseek-ai/dsh-llm'
import { import {
agent as createAcpAgentApp, agent as createAcpAgentApp,
@ -45,7 +46,7 @@ import {
type Stream, type Stream,
} from '@agentclientprotocol/sdk' } from '@agentclientprotocol/sdk'
import type { ModelSelection } from '@deepseek-ai/dsh-agent' import type { ModelSelection } from '@deepseek-ai/dsh-agent'
import { SessionId } from '@deepseek-ai/dsh-session' import type { SessionId } from '@deepseek-ai/dsh-session'
import type {} from '@deepseek-ai/dsh-session-persistence' import type {} from '@deepseek-ai/dsh-session-persistence'
// Side-effect type import: declaration-merges the approval waterfall answered below. // Side-effect type import: declaration-merges the approval waterfall answered below.
import type {} from '@deepseek-ai/dsh-user-approval' import type {} from '@deepseek-ai/dsh-user-approval'
@ -195,7 +196,7 @@ export function apply(ctx: Context, config: AcpConfig): void {
async newSession(params: NewSessionRequest, signal: AbortSignal): Promise<NewSessionResponse> { async newSession(params: NewSessionRequest, signal: AbortSignal): Promise<NewSessionResponse> {
assertOpen() assertOpen()
validateWorkspaceParams(params) validateWorkspaceParams(params)
const sessionId = SessionId(randomUUID()) const sessionId = brandString<SessionId>(randomUUID())
// No preset composition: the ACP bundle keeps the model-facing rows in // No preset composition: the ACP bundle keeps the model-facing rows in
// the host plane, so this agent reads them from the global layer. A // the host plane, so this agent reads them from the global layer. A
// deployment that configures a roster has to join one here first // deployment that configures a roster has to join one here first
@ -237,7 +238,7 @@ export function apply(ctx: Context, config: AcpConfig): void {
async resumeSession(params: ResumeSessionRequest, signal: AbortSignal): Promise<ResumeSessionResponse> { async resumeSession(params: ResumeSessionRequest, signal: AbortSignal): Promise<ResumeSessionResponse> {
assertOpen() assertOpen()
validateWorkspaceParams(params) validateWorkspaceParams(params)
const sessionId = SessionId(params.sessionId) const sessionId = brandString<SessionId>(params.sessionId)
if (sessions.has(sessionId) || activating.has(sessionId) || ctx.sessions.get(sessionId) !== undefined) { if (sessions.has(sessionId) || activating.has(sessionId) || ctx.sessions.get(sessionId) !== undefined) {
throw invalidParams(`session is already active: ${sessionId}`) throw invalidParams(`session is already active: ${sessionId}`)
} }
@ -333,7 +334,7 @@ export function apply(ctx: Context, config: AcpConfig): void {
signal: AbortSignal, signal: AbortSignal,
): Promise<SetSessionConfigOptionResponse> { ): Promise<SetSessionConfigOptionResponse> {
assertOpen() assertOpen()
const record = requireSession(SessionId(params.sessionId)) const record = requireSession(brandString<SessionId>(params.sessionId))
try { try {
return { configOptions: await record.setConfig(params.configId, params.value, signal) } return { configOptions: await record.setConfig(params.configId, params.value, signal) }
} catch (error: unknown) { } catch (error: unknown) {
@ -344,7 +345,7 @@ export function apply(ctx: Context, config: AcpConfig): void {
async closeSession(params: CloseSessionRequest): Promise<CloseSessionResponse> { async closeSession(params: CloseSessionRequest): Promise<CloseSessionResponse> {
assertOpen() assertOpen()
const sessionId = SessionId(params.sessionId) const sessionId = brandString<SessionId>(params.sessionId)
const record = requireSession(sessionId) const record = requireSession(sessionId)
try { try {
await record.close('ACP session closed') await record.close('ACP session closed')
@ -358,12 +359,12 @@ export function apply(ctx: Context, config: AcpConfig): void {
async prompt(params: PromptRequest, requestSignal: AbortSignal): Promise<PromptResponse> { async prompt(params: PromptRequest, requestSignal: AbortSignal): Promise<PromptResponse> {
assertOpen() assertOpen()
const record = requireSession(SessionId(params.sessionId)) const record = requireSession(brandString<SessionId>(params.sessionId))
return record.prompt(params, imagePromptEnabled, requestSignal) return record.prompt(params, imagePromptEnabled, requestSignal)
}, },
cancel(params: CancelNotification): Promise<void> { cancel(params: CancelNotification): Promise<void> {
sessions.get(SessionId(params.sessionId))?.cancel() sessions.get(brandString<SessionId>(params.sessionId))?.cancel()
return Promise.resolve() return Promise.resolve()
}, },
} }

View file

@ -18,7 +18,7 @@ import Group from '@deepseek-ai/cordis-plugin-group'
import { dshHomePath, resolveDshHome } from '@deepseek-ai/dsh-home-paths' import { dshHomePath, resolveDshHome } from '@deepseek-ai/dsh-home-paths'
import { createLaunchEnvironmentSnapshot, type LaunchEnvironmentSnapshot } from '@deepseek-ai/dsh-launch-environment' import { createLaunchEnvironmentSnapshot, type LaunchEnvironmentSnapshot } from '@deepseek-ai/dsh-launch-environment'
import type {} from '@deepseek-ai/cordis-plugin-hmr' import type {} from '@deepseek-ai/cordis-plugin-hmr'
import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import type {} from '@deepseek-ai/dsh-system-prompt'
declare module '@deepseek-ai/cordis' { declare module '@deepseek-ai/cordis' {
interface Context { interface Context {
@ -840,7 +840,7 @@ export function addHarnessSourceSection(ctx: Context, sourceRoot: string): (() =
if (systemPrompt === undefined) return undefined if (systemPrompt === undefined) return undefined
return systemPrompt.section({ return systemPrompt.section({
name: HARNESS_SOURCE_SECTION, name: HARNESS_SOURCE_SECTION,
order: FIRST_PARTY_SECTION_ORDER.HARNESS_SOURCE, order: systemPrompt.getSectionOrder('HARNESS_SOURCE'),
text: `The DeepSeek Harness implementation checkout is at ${sourceRoot}. The checkout location and current working directory are separate values and may differ; never infer the working directory from this path. Use pwd to determine the current working directory. Use this checkout only to inspect or extend DSH itself.`, text: `The DeepSeek Harness implementation checkout is at ${sourceRoot}. The checkout location and current working directory are separate values and may differ; never infer the working directory from this path. Use pwd to determine the current working directory. Use this checkout only to inspect or extend DSH itself.`,
}) })
} }

View file

@ -11,12 +11,13 @@
import { randomUUID } from 'node:crypto' import { randomUUID } from 'node:crypto'
import type { Context } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery' import z from '@deepseek-ai/schemastery'
import { brandString } from '@deepseek-ai/dsh-brand'
import { installModelSelection } from '@deepseek-ai/dsh-agent' import { installModelSelection } from '@deepseek-ai/dsh-agent'
import type { Agent, ModelSelectionRef } from '@deepseek-ai/dsh-agent' import type { Agent, ModelSelectionRef } from '@deepseek-ai/dsh-agent'
import type {} from '@deepseek-ai/dsh-agent-default-model' import type {} from '@deepseek-ai/dsh-agent-default-model'
import { assertNever, createUserMessage } from '@deepseek-ai/dsh-llm' import { createUserMessage } from '@deepseek-ai/dsh-llm'
import { SessionId } from '@deepseek-ai/dsh-session' import { assertNever } from '@deepseek-ai/dsh-util-values'
import type { SessionEvent } from '@deepseek-ai/dsh-session' import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session'
// Empty type imports carry the loader Context merge for the settlement await // Empty type imports carry the loader Context merge for the settlement await
// and the cmdline Context merge for the appExit host value. // and the cmdline Context merge for the appExit host value.
import type {} from '@deepseek-ai/cordis-plugin-loader' import type {} from '@deepseek-ai/cordis-plugin-loader'
@ -175,7 +176,7 @@ async function run(ctx: Context, task: string, io: HeadlessIo): Promise<void> {
// that DOES configure one has to join it here first // that DOES configure one has to join it here first
// (@deepseek-ai/dsh-agent-presets README, "Composing a child agent"). // (@deepseek-ai/dsh-agent-presets README, "Composing a child agent").
const { agent } = await agents.create({ const { agent } = await agents.create({
sessionId: SessionId(`session-${randomUUID()}`), sessionId: brandString<SessionId>(`session-${randomUUID()}`),
meta: { cwd: process.cwd() }, meta: { cwd: process.cwd() },
agentOptions: { provider: selection.provider, model: selection.model }, agentOptions: { provider: selection.provider, model: selection.model },
setup: (agentCtx) => { setup: (agentCtx) => {

View file

@ -25,7 +25,6 @@ import { launchEnvironmentOf } from '@deepseek-ai/dsh-launch-environment'
import { scrubbedParentEnv } from '@deepseek-ai/dsh-subprocess' import { scrubbedParentEnv } from '@deepseek-ai/dsh-subprocess'
import type {} from '@deepseek-ai/cordis-plugin-loader' import type {} from '@deepseek-ai/cordis-plugin-loader'
import type {} from '@deepseek-ai/dsh-host-webserver' import type {} from '@deepseek-ai/dsh-host-webserver'
import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt'
import type {} from '@deepseek-ai/dsh-shell-env' import type {} from '@deepseek-ai/dsh-shell-env'
/** Stable Cordis plugin name. */ /** Stable Cordis plugin name. */
@ -245,7 +244,7 @@ export function apply(ctx: Context, config: Config): void {
addHarnessSourceSection(promptCtx, SOURCE_ROOT) addHarnessSourceSection(promptCtx, SOURCE_ROOT)
promptCtx.systemPrompt.section({ promptCtx.systemPrompt.section({
name: 'app:web-surface', name: 'app:web-surface',
order: FIRST_PARTY_SECTION_ORDER.WEB_SURFACE, order: promptCtx.systemPrompt.getSectionOrder('WEB_SURFACE'),
text: () => webSurfacePrompt(localWebUrl(promptCtx)), text: () => webSurfacePrompt(localWebUrl(promptCtx)),
}) })
}) })

View file

@ -5,7 +5,8 @@ import {
createToolResultMessage, createToolResultMessage,
createUserMessage, createUserMessage,
} from '@deepseek-ai/dsh-llm/message' } from '@deepseek-ai/dsh-llm/message'
import { ToolCallId, type MessageId } from '@deepseek-ai/dsh-llm/brand' import { brandString } from '@deepseek-ai/dsh-brand'
import type { MessageId, ToolCallId } from '@deepseek-ai/dsh-llm/brand'
import type { import type {
AssistantMessage, AssistantMessage,
ContentBlock, ContentBlock,
@ -17,11 +18,11 @@ import type {
} from '@deepseek-ai/dsh-llm' } from '@deepseek-ai/dsh-llm'
import type { AttachmentIdType, ImageAttachmentRef } from '@deepseek-ai/dsh-attachment' import type { AttachmentIdType, ImageAttachmentRef } from '@deepseek-ai/dsh-attachment'
import type { import type {
JsonValue,
SessionEvent, SessionEvent,
SessionHeader, SessionHeader,
SessionId, SessionId,
} from '@deepseek-ai/dsh-session/types' } from '@deepseek-ai/dsh-session/types'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
import { isChunkRow, packChunkRuns } from '@deepseek-ai/dsh-session/chunk-rows' import { isChunkRow, packChunkRuns } from '@deepseek-ai/dsh-session/chunk-rows'
import type { ChunkRow } from '@deepseek-ai/dsh-session/chunk-rows' import type { ChunkRow } from '@deepseek-ai/dsh-session/chunk-rows'
import type { TodoItem } from '@deepseek-ai/dsh-tool-todo/client' import type { TodoItem } from '@deepseek-ai/dsh-tool-todo/client'
@ -355,7 +356,7 @@ function assistantMessage(content: ContentBlock[], model = 'fx-1'): AssistantMes
} }
function toolResultMessage(callId: string, content: ContentBlock[], isError: boolean): ToolResultMessage { function toolResultMessage(callId: string, content: ContentBlock[], isError: boolean): ToolResultMessage {
return createToolResultMessage({ callId: ToolCallId(callId), content, isError }) return createToolResultMessage({ callId: brandString<ToolCallId>(callId), content, isError })
} }
const MARKDOWN_FIXTURE = [ const MARKDOWN_FIXTURE = [

View file

@ -1,7 +1,7 @@
/** Host registration for the browser locale preference. */ /** Host registration for the browser locale preference. */
import type { Context } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis'
import { settingsNamespace } from '@deepseek-ai/dsh-settings' import type {} from '@deepseek-ai/dsh-settings'
import { LOCALE_SETTINGS_NAMESPACE, LocaleSettingsSchema } from './locale-settings.ts' import { LOCALE_SETTINGS_NAMESPACE, LocaleSettingsSchema } from './locale-settings.ts'
export { export {
@ -16,7 +16,7 @@ export {
export function apply(ctx: Context): void { export function apply(ctx: Context): void {
ctx.inject(['settings'], (settingsCtx) => { ctx.inject(['settings'], (settingsCtx) => {
settingsCtx.settings.register( settingsCtx.settings.register(
settingsNamespace(LOCALE_SETTINGS_NAMESPACE), LOCALE_SETTINGS_NAMESPACE,
LocaleSettingsSchema, LocaleSettingsSchema,
) )
}) })

View file

@ -1,6 +1,6 @@
import { Context } from '@deepseek-ai/cordis' import { Context } from '@deepseek-ai/cordis'
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import { SettingsProvider, settingsNamespace, type SettingsNamespace } from '@deepseek-ai/dsh-settings' import { SettingsProvider, type SettingsNamespace } from '@deepseek-ai/dsh-settings'
import { import {
LOCALE_SETTINGS_NAMESPACE, apply, LOCALE_SETTINGS_NAMESPACE, apply,
} from '@deepseek-ai/dsh-client-locale' } from '@deepseek-ai/dsh-client-locale'
@ -19,7 +19,7 @@ describe('locale host', () => {
await ctx.plugin(MemorySettings).await() await ctx.plugin(MemorySettings).await()
const fiber = ctx.plugin({ apply }) const fiber = ctx.plugin({ apply })
await fiber.await() await fiber.await()
const ns = settingsNamespace(LOCALE_SETTINGS_NAMESPACE) const ns = LOCALE_SETTINGS_NAMESPACE
expect(ctx.settings.get(ns)).toEqual({}) expect(ctx.settings.get(ns)).toEqual({})
await ctx.settings.update(ns, { preference: 'en' }) await ctx.settings.update(ns, { preference: 'en' })
expect(ctx.settings.get(ns)).toEqual({ preference: 'en' }) expect(ctx.settings.get(ns)).toEqual({ preference: 'en' })

View file

@ -58,7 +58,7 @@ function styleInjectionModule(
* Everything else under @deepseek-ai/* is either a module-table entry * Everything else under @deepseek-ai/* is either a module-table entry
* (external) or a leak the purity gate rejects. * (external) or a leak the purity gate rejects.
*/ */
export const INLINE_SAFE = /^(?:@deepseek-ai\/dsh-(?:file-reference|session|llm|tools|brand|deque|typert-protocol|util-crypto|util-workspace-path)(?:\/|$)|@deepseek-ai\/dsh-token-meter\/client$)/ export const INLINE_SAFE = /^(?:@deepseek-ai\/dsh-(?:file-reference|session|llm|tools|brand|deque|typert-protocol|util-crypto|util-values|util-workspace-path)(?:\/|$)|@deepseek-ai\/dsh-token-meter\/client$)/
/** /**
* Vendored framework libraries: rescoped into @deepseek-ai, so the gate below * Vendored framework libraries: rescoped into @deepseek-ai, so the gate below

View file

@ -1,7 +1,7 @@
/** Host registration for browser Chat preferences. */ /** Host registration for browser Chat preferences. */
import type { Context } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis'
import { settingsNamespace } from '@deepseek-ai/dsh-settings' import type {} from '@deepseek-ai/dsh-settings'
import { CHAT_SETTINGS_NAMESPACE, ChatSettingsSchema } from './chat-settings.ts' import { CHAT_SETTINGS_NAMESPACE, ChatSettingsSchema } from './chat-settings.ts'
export { export {
@ -13,7 +13,7 @@ export {
export function apply(ctx: Context): void { export function apply(ctx: Context): void {
ctx.inject(['settings'], (settingsCtx) => { ctx.inject(['settings'], (settingsCtx) => {
settingsCtx.settings.register( settingsCtx.settings.register(
settingsNamespace(CHAT_SETTINGS_NAMESPACE), CHAT_SETTINGS_NAMESPACE,
ChatSettingsSchema, ChatSettingsSchema,
) )
}) })

View file

@ -1,6 +1,6 @@
import { Context } from '@deepseek-ai/cordis' import { Context } from '@deepseek-ai/cordis'
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import { SettingsProvider, settingsNamespace, type SettingsNamespace } from '@deepseek-ai/dsh-settings' import { SettingsProvider, type SettingsNamespace } from '@deepseek-ai/dsh-settings'
import { import {
CHAT_SETTINGS_NAMESPACE, DEFAULT_TRANSCRIPT_VIEW_MODE, apply, CHAT_SETTINGS_NAMESPACE, DEFAULT_TRANSCRIPT_VIEW_MODE, apply,
} from '../src/index.ts' } from '../src/index.ts'
@ -19,7 +19,7 @@ describe('ui-chat Host settings', () => {
await ctx.plugin(MemorySettings).await() await ctx.plugin(MemorySettings).await()
const fiber = ctx.plugin({ apply }) const fiber = ctx.plugin({ apply })
await fiber.await() await fiber.await()
const ns = settingsNamespace(CHAT_SETTINGS_NAMESPACE) const ns = CHAT_SETTINGS_NAMESPACE
expect(ctx.settings.get(ns)).toEqual({ transcriptView: DEFAULT_TRANSCRIPT_VIEW_MODE }) expect(ctx.settings.get(ns)).toEqual({ transcriptView: DEFAULT_TRANSCRIPT_VIEW_MODE })
await ctx.settings.update(ns, { transcriptView: 'normal' }) await ctx.settings.update(ns, { transcriptView: 'normal' })

View file

@ -1,7 +1,7 @@
/** Host registration for browser conversation preferences. */ /** Host registration for browser conversation preferences. */
import type { Context } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis'
import { settingsNamespace } from '@deepseek-ai/dsh-settings' import type {} from '@deepseek-ai/dsh-settings'
import { CONVERSATION_SETTINGS_NAMESPACE, ConversationSettingsSchema } from './submission-settings.ts' import { CONVERSATION_SETTINGS_NAMESPACE, ConversationSettingsSchema } from './submission-settings.ts'
export { export {
@ -16,7 +16,7 @@ export {
export function apply(ctx: Context): void { export function apply(ctx: Context): void {
ctx.inject(['settings'], (settingsCtx) => { ctx.inject(['settings'], (settingsCtx) => {
settingsCtx.settings.register( settingsCtx.settings.register(
settingsNamespace(CONVERSATION_SETTINGS_NAMESPACE), CONVERSATION_SETTINGS_NAMESPACE,
ConversationSettingsSchema, ConversationSettingsSchema,
) )
}) })

View file

@ -1,6 +1,6 @@
import { Context } from '@deepseek-ai/cordis' import { Context } from '@deepseek-ai/cordis'
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import { SettingsProvider, settingsNamespace, type SettingsNamespace } from '@deepseek-ai/dsh-settings' import { SettingsProvider, type SettingsNamespace } from '@deepseek-ai/dsh-settings'
import { import {
CONVERSATION_SETTINGS_NAMESPACE, DEFAULT_BUSY_ENTER_BEHAVIOR, apply, CONVERSATION_SETTINGS_NAMESPACE, DEFAULT_BUSY_ENTER_BEHAVIOR, apply,
} from '@deepseek-ai/dsh-client-ui-conversation' } from '@deepseek-ai/dsh-client-ui-conversation'
@ -19,7 +19,7 @@ describe('ui-conversation host', () => {
await ctx.plugin(MemorySettings).await() await ctx.plugin(MemorySettings).await()
const fiber = ctx.plugin({ apply }) const fiber = ctx.plugin({ apply })
await fiber.await() await fiber.await()
const ns = settingsNamespace(CONVERSATION_SETTINGS_NAMESPACE) const ns = CONVERSATION_SETTINGS_NAMESPACE
expect(ctx.settings.get(ns)).toEqual({ busyEnter: DEFAULT_BUSY_ENTER_BEHAVIOR }) expect(ctx.settings.get(ns)).toEqual({ busyEnter: DEFAULT_BUSY_ENTER_BEHAVIOR })
await ctx.settings.update(ns, { busyEnter: 'steer' }) await ctx.settings.update(ns, { busyEnter: 'steer' })
expect(ctx.settings.get(ns)).toEqual({ busyEnter: 'steer' }) expect(ctx.settings.get(ns)).toEqual({ busyEnter: 'steer' })

View file

@ -6,7 +6,7 @@
*/ */
import type { Context } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis'
import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import type {} from '@deepseek-ai/dsh-system-prompt'
/** Services required for the model guidance paired with the browser renderer. */ /** Services required for the model guidance paired with the browser renderer. */
export const inject = ['systemPrompt'] export const inject = ['systemPrompt']
@ -22,7 +22,7 @@ const FILE_REFERENCE_PROMPT = 'When you successfully create or modify files, men
export function apply(ctx: Context): void { export function apply(ctx: Context): void {
ctx.systemPrompt.section({ ctx.systemPrompt.section({
name: 'ui:deliverable-file-references', name: 'ui:deliverable-file-references',
order: FIRST_PARTY_SECTION_ORDER.DELIVERABLE_FILE_REFERENCES, order: ctx.systemPrompt.getSectionOrder('DELIVERABLE_FILE_REFERENCES'),
text: FILE_REFERENCE_PROMPT, text: FILE_REFERENCE_PROMPT,
}) })
} }

View file

@ -2,7 +2,7 @@
import type { Context } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery' import z from '@deepseek-ai/schemastery'
import { settingsNamespace } from '@deepseek-ai/dsh-settings' import type {} from '@deepseek-ai/dsh-settings'
/** Durable settings namespace for product-wide GUI onboarding facts. */ /** Durable settings namespace for product-wide GUI onboarding facts. */
const ONBOARDING_SETTINGS_NAMESPACE = 'ui-onboarding' const ONBOARDING_SETTINGS_NAMESPACE = 'ui-onboarding'
@ -20,7 +20,7 @@ const OnboardingSettingsSchema: z<OnboardingSettings> = z.object({
export function apply(ctx: Context): void { export function apply(ctx: Context): void {
ctx.inject(['settings'], (settingsCtx) => { ctx.inject(['settings'], (settingsCtx) => {
settingsCtx.settings.register( settingsCtx.settings.register(
settingsNamespace(ONBOARDING_SETTINGS_NAMESPACE), ONBOARDING_SETTINGS_NAMESPACE,
OnboardingSettingsSchema, OnboardingSettingsSchema,
) )
}) })

View file

@ -1,6 +1,6 @@
import { Context } from '@deepseek-ai/cordis' import { Context } from '@deepseek-ai/cordis'
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import { SettingsProvider, settingsNamespace, type SettingsNamespace } from '@deepseek-ai/dsh-settings' import { SettingsProvider, type SettingsNamespace } from '@deepseek-ai/dsh-settings'
import { apply } from '../src/index.ts' import { apply } from '../src/index.ts'
/** Mirrors the module-local namespace id in src/index.ts. */ /** Mirrors the module-local namespace id in src/index.ts. */
@ -21,11 +21,11 @@ describe('ui-settings-general host', () => {
const fiber = ctx.plugin({ apply }) const fiber = ctx.plugin({ apply })
await fiber.await() await fiber.await()
expect(ctx.settings.describe().map(row => row.ns)).toContain( expect(ctx.settings.describe().map(row => row.ns)).toContain(
settingsNamespace(ONBOARDING_SETTINGS_NAMESPACE), ONBOARDING_SETTINGS_NAMESPACE,
) )
await fiber.dispose() await fiber.dispose()
expect(ctx.settings.describe().map(row => row.ns)).not.toContain( expect(ctx.settings.describe().map(row => row.ns)).not.toContain(
settingsNamespace(ONBOARDING_SETTINGS_NAMESPACE), ONBOARDING_SETTINGS_NAMESPACE,
) )
}) })
}) })

View file

@ -23,7 +23,7 @@
import { useState } from 'react' import { useState } from 'react'
import type { ReactNode } from 'react' import type { ReactNode } from 'react'
import type { JsonValue } from '@deepseek-ai/dsh-api-remotes/client' import type { JsonValue } from '@deepseek-ai/dsh-util-values'
import { apiKeyFailure } from './apiKey.ts' import { apiKeyFailure } from './apiKey.ts'
import { EditorFooter } from './EditorFooter.tsx' import { EditorFooter } from './EditorFooter.tsx'
import { validateDeepSeekModels } from './DeepSeekModelsEditor.tsx' import { validateDeepSeekModels } from './DeepSeekModelsEditor.tsx'

View file

@ -24,8 +24,9 @@
import { useEffect, useMemo, useState } from 'react' import { useEffect, useMemo, useState } from 'react'
import type { ReactNode } from 'react' import type { ReactNode } from 'react'
import type { import type {
CredentialInfo, JsonValue, SettingsNamespaceView, SettingsPathOpView, CredentialInfo, SettingsNamespaceView, SettingsPathOpView,
} from '@deepseek-ai/dsh-api-remotes/client' } from '@deepseek-ai/dsh-api-remotes/client'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
import { import {
DeepSeekModelsEditor, modelDrafts, validateDeepSeekModels, DeepSeekModelsEditor, modelDrafts, validateDeepSeekModels,
} from './DeepSeekModelsEditor.tsx' } from './DeepSeekModelsEditor.tsx'

View file

@ -5,8 +5,9 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
import Schema from '@deepseek-ai/schemastery' import Schema from '@deepseek-ai/schemastery'
import { bindSnapshotSelector, RemoteError } from '@deepseek-ai/dsh-client-test-runtime' import { bindSnapshotSelector, RemoteError } from '@deepseek-ai/dsh-client-test-runtime'
import type { import type {
CredentialInfo, JsonValue, RemoteResult, SettingsNamespaceView, CredentialInfo, RemoteResult, SettingsNamespaceView,
} from '@deepseek-ai/dsh-api-remotes/client' } from '@deepseek-ai/dsh-api-remotes/client'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
import { import {
ModelsSection, needsSetup, providerCopy, providerTargetLabel, removeProviderProfile, ModelsSection, needsSetup, providerCopy, providerTargetLabel, removeProviderProfile,
} from '../src/client/ModelsSection.tsx' } from '../src/client/ModelsSection.tsx'

View file

@ -3,7 +3,8 @@
import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react' import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
import { afterEach, describe, expect, it, vi } from 'vitest' import { afterEach, describe, expect, it, vi } from 'vitest'
import Schema from '@deepseek-ai/schemastery' import Schema from '@deepseek-ai/schemastery'
import type { JsonValue, SettingsNamespaceView } from '@deepseek-ai/dsh-api-remotes/client' import type { SettingsNamespaceView } from '@deepseek-ai/dsh-api-remotes/client'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
import { bindSnapshotSelector, RemoteError } from '@deepseek-ai/dsh-client-test-runtime' import { bindSnapshotSelector, RemoteError } from '@deepseek-ai/dsh-client-test-runtime'
import { DeepSeekOnboardingDialog } from '../src/client/DeepSeekOnboardingDialog.tsx' import { DeepSeekOnboardingDialog } from '../src/client/DeepSeekOnboardingDialog.tsx'
import type { DeepSeekOnboardingDialogProps } from '../src/client/DeepSeekOnboardingDialog.tsx' import type { DeepSeekOnboardingDialogProps } from '../src/client/DeepSeekOnboardingDialog.tsx'

View file

@ -4,7 +4,8 @@ import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/re
import { afterEach, describe, expect, it, vi } from 'vitest' import { afterEach, describe, expect, it, vi } from 'vitest'
import Schema from '@deepseek-ai/schemastery' import Schema from '@deepseek-ai/schemastery'
import { bindSnapshotSelector, RemoteError } from '@deepseek-ai/dsh-client-test-runtime' import { bindSnapshotSelector, RemoteError } from '@deepseek-ai/dsh-client-test-runtime'
import type { JsonValue, SettingsNamespaceView } from '@deepseek-ai/dsh-api-remotes/client' import type { SettingsNamespaceView } from '@deepseek-ai/dsh-api-remotes/client'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
import { ModelsSection, providerCopy } from '../src/client/ModelsSection.tsx' import { ModelsSection, providerCopy } from '../src/client/ModelsSection.tsx'
import type { ModelsSectionInjected, ModelsSectionProps } from '../src/client/ModelsSection.tsx' import type { ModelsSectionInjected, ModelsSectionProps } from '../src/client/ModelsSection.tsx'
import { CustomProviderCard } from '../src/client/CustomProviderCard.tsx' import { CustomProviderCard } from '../src/client/CustomProviderCard.tsx'

View file

@ -9,9 +9,10 @@
import { Service } from '@deepseek-ai/cordis' import { Service } from '@deepseek-ai/cordis'
import type { Context } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis'
import type { import type {
JsonValue, SettingsNamespaceView, SettingsPathOpView, SettingsNamespaceView, SettingsPathOpView,
} from '@deepseek-ai/dsh-api-remotes/client' } from '@deepseek-ai/dsh-api-remotes/client'
import { createSnapshotStore, type SnapshotStore } from '@deepseek-ai/dsh-client-store' import { createSnapshotStore, type SnapshotStore } from '@deepseek-ai/dsh-client-store'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
// Type-only, and deliberately NOT `@deepseek-ai/dsh-api-remotes/client`: this // Type-only, and deliberately NOT `@deepseek-ai/dsh-api-remotes/client`: this
// package is reachable from the Host build graph through its feature-package // package is reachable from the Host build graph through its feature-package
// callers, and api-remotes' Client face imports a Host-tsdown-generated // callers, and api-remotes' Client face imports a Host-tsdown-generated

View file

@ -2,9 +2,10 @@ import { Context } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery' import z from '@deepseek-ai/schemastery'
import { describe, expect, it, vi } from 'vitest' import { describe, expect, it, vi } from 'vitest'
import type { import type {
JsonValue, SettingsNamespaceView, SettingsPathOpView, SettingsNamespaceView, SettingsPathOpView,
} from '@deepseek-ai/dsh-api-remotes/client' } from '@deepseek-ai/dsh-api-remotes/client'
import { RemoteError, TestRemote } from '@deepseek-ai/dsh-client-test-runtime' import { RemoteError, TestRemote } from '@deepseek-ai/dsh-client-test-runtime'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
import type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client' import type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client'
import { SettingsSchemaService } from '../src/client/schema.ts' import { SettingsSchemaService } from '../src/client/schema.ts'
import { SettingsScopeController, SettingsScopeBinder } from '../src/client/settings-scope.ts' import { SettingsScopeController, SettingsScopeBinder } from '../src/client/settings-scope.ts'

View file

@ -2,7 +2,7 @@
import type { Context } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis'
import type {} from '@deepseek-ai/dsh-host-webserver' import type {} from '@deepseek-ai/dsh-host-webserver'
import { settingsNamespace } from '@deepseek-ai/dsh-settings' import type {} from '@deepseek-ai/dsh-settings'
import { bootThemeInjection } from './boot-theme.ts' import { bootThemeInjection } from './boot-theme.ts'
import { import {
DEFAULT_FONT_SIZE, DEFAULT_PREFERENCE, THEME_SETTINGS_NAMESPACE, ThemeSettingsSchema, DEFAULT_FONT_SIZE, DEFAULT_PREFERENCE, THEME_SETTINGS_NAMESPACE, ThemeSettingsSchema,
@ -15,7 +15,7 @@ export {
type ThemePreference, type ThemeSettings, type ThemePreference, type ThemeSettings,
} from './theme-settings.ts' } from './theme-settings.ts'
const THEME_NAMESPACE = settingsNamespace(THEME_SETTINGS_NAMESPACE) const THEME_NAMESPACE = THEME_SETTINGS_NAMESPACE
/** Read the registered theme section or the schema defaults without a settings provider. */ /** Read the registered theme section or the schema defaults without a settings provider. */
function readSection(ctx: Context): { preference: ThemePreference; fontSize: number } { function readSection(ctx: Context): { preference: ThemePreference; fontSize: number } {

View file

@ -1,7 +1,7 @@
import { Context } from '@deepseek-ai/cordis' import { Context } from '@deepseek-ai/cordis'
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import type { IndexInjection } from '@deepseek-ai/dsh-host-webserver' import type { IndexInjection } from '@deepseek-ai/dsh-host-webserver'
import { SettingsProvider, settingsNamespace, type SettingsNamespace } from '@deepseek-ai/dsh-settings' import { SettingsProvider, type SettingsNamespace } from '@deepseek-ai/dsh-settings'
import { import {
DEFAULT_PREFERENCE, THEME_SETTINGS_NAMESPACE, apply, DEFAULT_PREFERENCE, THEME_SETTINGS_NAMESPACE, apply,
} from '@deepseek-ai/dsh-client-ui-theme' } from '@deepseek-ai/dsh-client-ui-theme'
@ -33,7 +33,7 @@ describe('ui-theme host', () => {
await ctx.plugin(MemorySettings).await() await ctx.plugin(MemorySettings).await()
const fiber = ctx.plugin({ apply }) const fiber = ctx.plugin({ apply })
await fiber.await() await fiber.await()
const ns = settingsNamespace(THEME_SETTINGS_NAMESPACE) const ns = THEME_SETTINGS_NAMESPACE
expect(ctx.settings.get(ns)).toEqual({ preference: DEFAULT_PREFERENCE, fontSize: 14 }) expect(ctx.settings.get(ns)).toEqual({ preference: DEFAULT_PREFERENCE, fontSize: 14 })
await ctx.settings.update(ns, { preference: 'dark', fontSize: 16 }) await ctx.settings.update(ns, { preference: 'dark', fontSize: 16 })
expect(ctx.settings.get(ns)).toEqual({ preference: 'dark', fontSize: 16 }) expect(ctx.settings.get(ns)).toEqual({ preference: 'dark', fontSize: 16 })
@ -54,7 +54,7 @@ describe('ui-theme host', () => {
expect(rows[0]).toMatchObject({ kind: 'script', placement: 'body' }) expect(rows[0]).toMatchObject({ kind: 'script', placement: 'body' })
expect(scriptText(rows[0])).toContain('const preference = "system"') expect(scriptText(rows[0])).toContain('const preference = "system"')
expect(scriptText(rows[0])).toContain('"14px"') expect(scriptText(rows[0])).toContain('"14px"')
await ctx.settings.update(settingsNamespace(THEME_SETTINGS_NAMESPACE), { preference: 'dark', fontSize: 17 }) await ctx.settings.update(THEME_SETTINGS_NAMESPACE, { preference: 'dark', fontSize: 17 })
expect(scriptText(collect(ctx)[0])).toContain('const preference = "dark"') expect(scriptText(collect(ctx)[0])).toContain('const preference = "dark"')
expect(scriptText(collect(ctx)[0])).toContain('"17px"') expect(scriptText(collect(ctx)[0])).toContain('"17px"')
await fiber.dispose() await fiber.dispose()

View file

@ -1,6 +1,6 @@
/** Test adapter for the production conversation.details.tool registration. */ /** Test adapter for the production conversation.details.tool registration. */
import type { SessionLiveEventEntry } from '@deepseek-ai/dsh-api-session-controller/client' import type { SessionLiveEventEntry } from '@deepseek-ai/dsh-api-session-controller/client'
import { isJsonValue, type JsonValue } from '@deepseek-ai/dsh-session' import { isJsonValue, type JsonValue } from '@deepseek-ai/dsh-util-values'
import type { import type {
ChatConversationViewNode, ChatSnapshot, ConversationNode, DetailsSlotProps, ChatConversationViewNode, ChatSnapshot, ConversationNode, DetailsSlotProps,
DetailsToolOwnerProps, RunningToolCall, ToolResultNode, DetailsToolOwnerProps, RunningToolCall, ToolResultNode,

View file

@ -15,7 +15,7 @@ import z from '@deepseek-ai/schemastery'
import { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout' import { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout'
import { CodeRuntime, DUNDER_MEMBER, PORTABLE_RESERVED_WORDS, RESERVED_BINDING_GLOBALS, RESERVED_ERROR_MEMBERS } from '@deepseek-ai/dsh-code-runtime' import { CodeRuntime, DUNDER_MEMBER, PORTABLE_RESERVED_WORDS, RESERVED_BINDING_GLOBALS, RESERVED_ERROR_MEMBERS } from '@deepseek-ai/dsh-code-runtime'
import type { CodeBindingNamespace, CodeJsonValue, CodeRunFailure, CodeRunRequest, CodeRunResult } from '@deepseek-ai/dsh-code-runtime' import type { CodeBindingNamespace, CodeJsonValue, CodeRunFailure, CodeRunRequest, CodeRunResult } from '@deepseek-ai/dsh-code-runtime'
import { snapshotJsonValue } from '@deepseek-ai/dsh-session' import { snapshotJsonValue } from '@deepseek-ai/dsh-util-values'
import type { ReplyMessage, WorkerBootData, WorkerToHost } from './protocol.ts' import type { ReplyMessage, WorkerBootData, WorkerToHost } from './protocol.ts'
import { jsonStringBytesUpTo, jsonValueBytesUpTo, truncateJsonStringBytes } from './output-json.ts' import { jsonStringBytesUpTo, jsonValueBytesUpTo, truncateJsonStringBytes } from './output-json.ts'
import { decodeWorkerJson, encodeWorkerJson } from './worker-json.ts' import { decodeWorkerJson, encodeWorkerJson } from './worker-json.ts'

View file

@ -1,6 +1,6 @@
import { runInNewContext } from 'node:vm' import { runInNewContext } from 'node:vm'
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import { snapshotJsonValue } from '@deepseek-ai/dsh-session' import { snapshotJsonValue } from '@deepseek-ai/dsh-util-values'
import { decodeWorkerJson, encodeWorkerJson, snapshotCodeJsonValue } from '../src/worker-json.ts' import { decodeWorkerJson, encodeWorkerJson, snapshotCodeJsonValue } from '../src/worker-json.ts'
describe('snapshotCodeJsonValue', () => { describe('snapshotCodeJsonValue', () => {

View file

@ -4,8 +4,8 @@
* @module @deepseek-ai/dsh-compaction-basic/config * @module @deepseek-ai/dsh-compaction-basic/config
*/ */
import { deepFreeze } from '@deepseek-ai/dsh-llm'
import type { LlmCallConfig } from '@deepseek-ai/dsh-llm' import type { LlmCallConfig } from '@deepseek-ai/dsh-llm'
import { deepFreeze } from '@deepseek-ai/dsh-util-values'
import type { import type {
BasicCompactionConfig, BasicCompactionConfig,
CompactionPolicyConfig, CompactionPolicyConfig,

View file

@ -10,8 +10,9 @@ import { CompactionEngine, ManualCompactionError } from '@deepseek-ai/dsh-compac
import type { CompactionResult, CompactionTrigger } from '@deepseek-ai/dsh-compaction' import type { CompactionResult, CompactionTrigger } from '@deepseek-ai/dsh-compaction'
import type { TokenMeter } from '@deepseek-ai/dsh-token-meter' import type { TokenMeter } from '@deepseek-ai/dsh-token-meter'
import type { Session } from '@deepseek-ai/dsh-session' import type { Session } from '@deepseek-ai/dsh-session'
import { CONTEXT_WINDOW_EXCEEDED_CODE, assertNever } from '@deepseek-ai/dsh-llm' import { CONTEXT_WINDOW_EXCEEDED_CODE } from '@deepseek-ai/dsh-llm'
import type { LlmCallConfig } from '@deepseek-ai/dsh-llm' import type { LlmCallConfig } from '@deepseek-ai/dsh-llm'
import { assertNever } from '@deepseek-ai/dsh-util-values'
import type { Agent, PreStepDecision } from '@deepseek-ai/dsh-agent' import type { Agent, PreStepDecision } from '@deepseek-ai/dsh-agent'
import type { CommandId } from '@deepseek-ai/dsh-commands/brand' import type { CommandId } from '@deepseek-ai/dsh-commands/brand'
// Type-only: makes the optional sibling service available to `ctx.get()`. // Type-only: makes the optional sibling service available to `ctx.get()`.

View file

@ -1,6 +1,6 @@
/** Configuration resolution for deterministic tool-result pruning. */ /** Configuration resolution for deterministic tool-result pruning. */
import { deepFreeze } from '@deepseek-ai/dsh-llm' import { deepFreeze } from '@deepseek-ai/dsh-util-values'
import type { ResolvedConfig, ToolResultPruneConfig } from './types.ts' import type { ResolvedConfig, ToolResultPruneConfig } from './types.ts'
/** Fixed marker substituted for every removed middle span. */ /** Fixed marker substituted for every removed middle span. */

View file

@ -8,8 +8,8 @@ import { createReadStream } from 'node:fs'
import { stat } from 'node:fs/promises' import { stat } from 'node:fs/promises'
import { dirname, isAbsolute, join, relative, resolve } from 'node:path' import { dirname, isAbsolute, join, relative, resolve } from 'node:path'
import type { FileSystem, FsInfo, FsTarget, FsVersion } from '@deepseek-ai/dsh-fs' import type { FileSystem, FsInfo, FsTarget, FsVersion } from '@deepseek-ai/dsh-fs'
import { assertNever } from '@deepseek-ai/dsh-llm'
import { dshHomeDisplay } from '@deepseek-ai/dsh-home-paths' import { dshHomeDisplay } from '@deepseek-ai/dsh-home-paths'
import { assertNever } from '@deepseek-ai/dsh-util-values'
import { resolveConfig, resolveDiscoveryConfig, type ResolvedConfig } from './config.ts' import { resolveConfig, resolveDiscoveryConfig, type ResolvedConfig } from './config.ts'
import { trimmedInstructionDigest } from './digest.ts' import { trimmedInstructionDigest } from './digest.ts'
import { import {

View file

@ -11,7 +11,6 @@ import FileReferenceService, {
FILE_REFERENCE_PROMPT, FILE_REFERENCE_PROMPT,
type FileReferenceCandidate, type FileReferenceCandidate,
} from '@deepseek-ai/dsh-file-reference' } from '@deepseek-ai/dsh-file-reference'
import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt'
import type {} from '@deepseek-ai/dsh-tools' import type {} from '@deepseek-ai/dsh-tools'
import { import {
DEFAULT_FILE_SEARCH_EXCLUDED_DIRECTORIES, DEFAULT_FILE_SEARCH_EXCLUDED_DIRECTORIES,
@ -69,7 +68,7 @@ export class LocalFileReferenceService extends FileReferenceService {
const fiber = agent.ctx.inject(['systemPrompt', 'tools'], (scope) => { const fiber = agent.ctx.inject(['systemPrompt', 'tools'], (scope) => {
scope.systemPrompt.section({ scope.systemPrompt.section({
name: 'context:file-reference', name: 'context:file-reference',
order: FIRST_PARTY_SECTION_ORDER.FILE_REFERENCE, order: scope.systemPrompt.getSectionOrder('FILE_REFERENCE'),
text: () => agent.ctx.tools.get('read', agent) === undefined ? '' : FILE_REFERENCE_PROMPT, text: () => agent.ctx.tools.get('read', agent) === undefined ? '' : FILE_REFERENCE_PROMPT,
}) })
}) })

View file

@ -2,8 +2,8 @@
import { isCompactCheckpointSource } from '@deepseek-ai/dsh-compaction' import { isCompactCheckpointSource } from '@deepseek-ai/dsh-compaction'
import type { SessionSurfaceSnapshot } from '@deepseek-ai/dsh-session-query' import type { SessionSurfaceSnapshot } from '@deepseek-ai/dsh-session-query'
import { assertNever } from '@deepseek-ai/dsh-llm'
import { TextRetainer } from '@deepseek-ai/dsh-output-retention' import { TextRetainer } from '@deepseek-ai/dsh-output-retention'
import { assertNever } from '@deepseek-ai/dsh-util-values'
import { stringifyTagSafeJson } from './serialization.ts' import { stringifyTagSafeJson } from './serialization.ts'
import type { ReferencedConversationItem } from './types.ts' import type { ReferencedConversationItem } from './types.ts'

View file

@ -1,6 +1,7 @@
/** Canonical session URI and inline mention encoding. */ /** Canonical session URI and inline mention encoding. */
import { SessionId, type SessionId as SessionIdType } from '@deepseek-ai/dsh-session' import { brandString } from '@deepseek-ai/dsh-brand'
import type { SessionId as SessionIdType } from '@deepseek-ai/dsh-session'
import { SessionReferenceError } from './config.ts' import { SessionReferenceError } from './config.ts'
import type { SessionReferenceInput } from './types.ts' import type { SessionReferenceInput } from './types.ts'
@ -31,7 +32,7 @@ export function decodeSessionReferenceUri(uri: string): SessionIdType {
try { try {
const parsed: unknown = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8')) const parsed: unknown = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'))
if (typeof parsed !== 'string') throw new TypeError('decoded session id is not a string') if (typeof parsed !== 'string') throw new TypeError('decoded session id is not a string')
const sessionId = SessionId(parsed) const sessionId = brandString<SessionIdType>(parsed)
if (encodeSessionReferenceUri(sessionId) !== uri) throw new TypeError('URI is not canonical') if (encodeSessionReferenceUri(sessionId) !== uri) throw new TypeError('URI is not canonical')
return sessionId return sessionId
} catch (error: unknown) { } catch (error: unknown) {

View file

@ -1,7 +1,7 @@
/** Browser-zone derivation and model-facing policy text for one open request turn. */ /** Browser-zone derivation and model-facing policy text for one open request turn. */
import { assertNever } from '@deepseek-ai/dsh-llm'
import type { UserMessage } from '@deepseek-ai/dsh-llm' import type { UserMessage } from '@deepseek-ai/dsh-llm'
import { assertNever } from '@deepseek-ai/dsh-util-values'
const IANA_TIME_ZONE = /^[A-Za-z][A-Za-z0-9_+.-]*(?:\/[A-Za-z0-9_+.-]+)+$/ const IANA_TIME_ZONE = /^[A-Za-z][A-Za-z0-9_+.-]*(?:\/[A-Za-z0-9_+.-]+)+$/

View file

@ -8,7 +8,7 @@ import { Context, Service } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery' import z from '@deepseek-ai/schemastery'
import type { ModelSelection } from '@deepseek-ai/dsh-agent' import type { ModelSelection } from '@deepseek-ai/dsh-agent'
import { ReasoningEffortId } from '@deepseek-ai/dsh-llm' import { ReasoningEffortId } from '@deepseek-ai/dsh-llm'
import { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings' import type {} from '@deepseek-ai/dsh-settings'
declare module '@deepseek-ai/cordis' { declare module '@deepseek-ai/cordis' {
interface Context { interface Context {
@ -18,7 +18,7 @@ declare module '@deepseek-ai/cordis' {
} }
/** Settings namespace carrying the default model selection for future Agents. */ /** Settings namespace carrying the default model selection for future Agents. */
export const AGENT_DEFAULT_MODEL_SETTINGS_NAMESPACE = settingsNamespace('agent-default-model') export const AGENT_DEFAULT_MODEL_SETTINGS_NAMESPACE = 'agent-default-model'
/** Stored and composed default model selection. */ /** Stored and composed default model selection. */
export interface AgentDefaultModelSettings { export interface AgentDefaultModelSettings {
@ -73,11 +73,13 @@ export class AgentDefaultModelConfig extends Service {
super(ctx, 'agentDefaultModel') super(ctx, 'agentDefaultModel')
const entry: AgentDefaultModelSettings = { provider: config.provider, model: config.model } const entry: AgentDefaultModelSettings = { provider: config.provider, model: config.model }
this.source = () => entry this.source = () => entry
installSettingsSection(ctx, AGENT_DEFAULT_MODEL_SETTINGS_NAMESPACE, AGENT_DEFAULT_MODEL_SETTINGS_SCHEMA, entry, { ctx.inject(['settings'], (settingsCtx) => {
setSource: (current) => { this.source = current }, settingsCtx.settings.installSection(ctx, AGENT_DEFAULT_MODEL_SETTINGS_NAMESPACE, AGENT_DEFAULT_MODEL_SETTINGS_SCHEMA, entry, {
// Every consumer reads through currentSelection(), so no registration-level fact setSource: (current) => { this.source = current },
// needs rebuilding when the settings document changes. // Every consumer reads through currentSelection(), so no registration-level fact
onChange: () => {}, // needs rebuilding when the settings document changes.
onChange: () => {},
})
}) })
} }

View file

@ -21,10 +21,10 @@ import {
BlockAssembler, BlockAssembler,
LlmError, LlmError,
createAssistantMessage, createAssistantMessage,
deepFreeze,
errorChain, errorChain,
markAgentLoopRequest, markAgentLoopRequest,
} from '@deepseek-ai/dsh-llm' } from '@deepseek-ai/dsh-llm'
import { deepFreeze } from '@deepseek-ai/dsh-util-values'
import type { Scope } from '@deepseek-ai/dsh-scope' import type { Scope } from '@deepseek-ai/dsh-scope'
import { createScope } from '@deepseek-ai/dsh-scope' import { createScope } from '@deepseek-ai/dsh-scope'
import type { EpochHeader, RequestContext, Session, SessionId, TurnEndReason, UserMessage } from '@deepseek-ai/dsh-session' import type { EpochHeader, RequestContext, Session, SessionId, TurnEndReason, UserMessage } from '@deepseek-ai/dsh-session'

View file

@ -9,6 +9,7 @@ import { Context, FiberState, Service } from '@deepseek-ai/cordis'
import { randomUUID } from 'node:crypto' import { randomUUID } from 'node:crypto'
import z from '@deepseek-ai/schemastery' import z from '@deepseek-ai/schemastery'
import { z as zod } from 'zod' import { z as zod } from 'zod'
import { brandString } from '@deepseek-ai/dsh-brand'
import { emitAgentEvent } from '@deepseek-ai/dsh-agent' import { emitAgentEvent } from '@deepseek-ai/dsh-agent'
import type { import type {
Agent, Agent,
@ -22,9 +23,9 @@ import type {
TurnBoundaryProjection, TurnBoundaryProjection,
} from '@deepseek-ai/dsh-agent' } from '@deepseek-ai/dsh-agent'
import { errorChain, ReasoningEffortId } from '@deepseek-ai/dsh-llm' import { errorChain, ReasoningEffortId } from '@deepseek-ai/dsh-llm'
import { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings' import type {} from '@deepseek-ai/dsh-settings'
import { SessionId, SessionPreparation } from '@deepseek-ai/dsh-session' import { SessionPreparation } from '@deepseek-ai/dsh-session'
import type { Session, SessionHeader } from '@deepseek-ai/dsh-session' import type { Session, SessionHeader, SessionId } from '@deepseek-ai/dsh-session'
import type {} from '@deepseek-ai/dsh-system-prompt' import type {} from '@deepseek-ai/dsh-system-prompt'
import type {} from '@deepseek-ai/dsh-tools' import type {} from '@deepseek-ai/dsh-tools'
import type {} from '@deepseek-ai/dsh-session-projection' import type {} from '@deepseek-ai/dsh-session-projection'
@ -289,7 +290,7 @@ function applyLauncherIdentities(
} }
/** Settings namespace carrying the tool-call parallelism a user owns. */ /** Settings namespace carrying the tool-call parallelism a user owns. */
export const AGENT_LOOP_SETTINGS_NAMESPACE = settingsNamespace('agent-loop') export const AGENT_LOOP_SETTINGS_NAMESPACE = 'agent-loop'
/** /**
* The agent-loop fields a user owns. Deliberately a strict subset of * The agent-loop fields a user owns. Deliberately a strict subset of
@ -389,16 +390,18 @@ export class AgentLoop extends Service implements AgentFactory {
return source().maxParallelToolCalls return source().maxParallelToolCalls
}, },
} }
installSettingsSection(ctx, AGENT_LOOP_SETTINGS_NAMESPACE, AGENT_LOOP_SETTINGS_SCHEMA, entry, { ctx.inject(['settings'], (settingsCtx) => {
// The schema admits any integer above zero; `resolveMaxParallelToolCalls` settingsCtx.settings.installSection(ctx, AGENT_LOOP_SETTINGS_NAMESPACE, AGENT_LOOP_SETTINGS_SCHEMA, entry, {
// owns the whole rule, so refusing here keeps the running scheduler on // The schema admits any integer above zero; `resolveMaxParallelToolCalls`
// its last good cap instead of failing at the next tool group. // owns the whole rule, so refusing here keeps the running scheduler on
validate: value => void resolveMaxParallelToolCalls(value.maxParallelToolCalls), // its last good cap instead of failing at the next tool group.
setSource: (current) => { validate: value => void resolveMaxParallelToolCalls(value.maxParallelToolCalls),
source = current setSource: (current) => {
}, source = current
// Nothing is derived from the cap: the getter above is the only reader. },
onChange: () => {}, // Nothing is derived from the cap: the getter above is the only reader.
onChange: () => {},
})
}) })
validateConfiguredAgents(this.config.agents) validateConfiguredAgents(this.config.agents)
// Register only after every config validation above has passed, so a // Register only after every config validation above has passed, so a
@ -415,7 +418,7 @@ export class AgentLoop extends Service implements AgentFactory {
for (const { id, sessionId, cwd, resumeSessionId, ...options } of this.config.agents) { for (const { id, sessionId, cwd, resumeSessionId, ...options } of this.config.agents) {
const meta = cwd === undefined ? {} : { cwd } const meta = cwd === undefined ? {} : { cwd }
if (resumeSessionId === undefined || resumeSessionId === '') { if (resumeSessionId === undefined || resumeSessionId === '') {
const configuredId = sessionId ?? SessionId(`${id}-session-${randomUUID()}`) const configuredId = sessionId ?? brandString<SessionId>(`${id}-session-${randomUUID()}`)
const persistence = sessionId === undefined ? undefined : ctx.get('sessionPersistence') const persistence = sessionId === undefined ? undefined : ctx.get('sessionPersistence')
if (persistence === undefined) { if (persistence === undefined) {
this.create(configuredId, options, meta) this.create(configuredId, options, meta)

View file

@ -12,9 +12,10 @@
*/ */
import type { Context } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis'
import { assertNever, createToolResultMessage, type ToolCallBlock } from '@deepseek-ai/dsh-llm' import { createToolResultMessage, type ToolCallBlock } from '@deepseek-ai/dsh-llm'
import type { Session, UserMessage } from '@deepseek-ai/dsh-session' import type { Session, UserMessage } from '@deepseek-ai/dsh-session'
import { TOOL_ABORTED_BEFORE_DISPATCH, TOOL_RUNTIME_SCHEDULER, type ToolExecutionInput, type ToolExecutionMode, type ToolExecutionResult, type ToolRunContext } from '@deepseek-ai/dsh-tools' import { TOOL_ABORTED_BEFORE_DISPATCH, TOOL_RUNTIME_SCHEDULER, type ToolExecutionInput, type ToolExecutionMode, type ToolExecutionResult, type ToolRunContext } from '@deepseek-ai/dsh-tools'
import { assertNever } from '@deepseek-ai/dsh-util-values'
/** One tool call after argument parsing, ready to schedule. */ /** One tool call after argument parsing, ready to schedule. */
interface PlannedCall { interface PlannedCall {

View file

@ -1,6 +1,6 @@
import { runInNewContext } from 'node:vm' import { runInNewContext } from 'node:vm'
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import { isJsonValue, snapshotJsonValue, type JsonValue } from '@deepseek-ai/dsh-session' import { isJsonValue, snapshotJsonValue, type JsonValue } from '@deepseek-ai/dsh-util-values'
function objectWithForgedIntrinsicPrototype(revoked = false): Record<string, unknown> { function objectWithForgedIntrinsicPrototype(revoked = false): Record<string, unknown> {
const prototype = Object.create(null) as Record<string, unknown> const prototype = Object.create(null) as Record<string, unknown>

View file

@ -9,11 +9,11 @@ import z from '@deepseek-ai/schemastery'
import { AnonymousEntries, NamedEntries, ScopedLayers, scopeOf, scopeTarget } from '@deepseek-ai/dsh-scope' import { AnonymousEntries, NamedEntries, ScopedLayers, scopeOf, scopeTarget } from '@deepseek-ai/dsh-scope'
import type { ScopeKey, ScopeLayer, Scoped } from '@deepseek-ai/dsh-scope' import type { ScopeKey, ScopeLayer, Scoped } from '@deepseek-ai/dsh-scope'
import type { ToolCallId, ContentBlock, ToolSchema } from '@deepseek-ai/dsh-llm' import type { ToolCallId, ContentBlock, ToolSchema } from '@deepseek-ai/dsh-llm'
import { assertNever, deepFreeze, HarnessError } from '@deepseek-ai/dsh-llm' import { HarnessError } from '@deepseek-ai/dsh-llm'
import type { Agent } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent'
import { snapshotJsonValue } from '@deepseek-ai/dsh-session' import type { UserMessage } from '@deepseek-ai/dsh-session'
import type { JsonValue, UserMessage } from '@deepseek-ai/dsh-session' import { assertNever, deepFreeze, snapshotJsonValue, type JsonValue } from '@deepseek-ai/dsh-util-values'
import { FIRST_PARTY_SECTION_ORDER, type ToolProviderResult } from '@deepseek-ai/dsh-system-prompt' import type { ToolProviderResult } from '@deepseek-ai/dsh-system-prompt'
import type { CodeRuntime } from '@deepseek-ai/dsh-code-runtime' import type { CodeRuntime } from '@deepseek-ai/dsh-code-runtime'
// Type-only: makes `ctx.get('approval')` resolve to the ApprovalService // Type-only: makes `ctx.get('approval')` resolve to the ApprovalService
// augmentation. The seam stays optional at runtime — see `serviceAsk`. // augmentation. The seam stays optional at runtime — see `serviceAsk`.
@ -21,7 +21,7 @@ import type {} from '@deepseek-ai/dsh-user-approval'
import type { ToolCallView, ToolResultView } from './presentation.ts' import type { ToolCallView, ToolResultView } from './presentation.ts'
import { assertSupportedJsonSchema, validateJsonSchemaValue } from './json-schema.ts' import { assertSupportedJsonSchema, validateJsonSchemaValue } from './json-schema.ts'
import type { JsonSchemaNode } from './json-schema.ts' import type { JsonSchemaNode } from './json-schema.ts'
import { createRunCodeTool, RUN_CODE_NAME, SDK_SECTION_ORDER } from './ptc.ts' import { createRunCodeTool, RUN_CODE_NAME } from './ptc.ts'
import type { CodeSdkLanguage } from './ptc.ts' import type { CodeSdkLanguage } from './ptc.ts'
import { renderToolsSdk } from './ts-types.ts' import { renderToolsSdk } from './ts-types.ts'
import type { ToolSdkSchema } from './ts-types.ts' import type { ToolSdkSchema } from './ts-types.ts'
@ -43,13 +43,6 @@ import { renderToolsSdkPy } from './py-types.ts'
* with its zh pair, plus this package's own README pair and the * with its zh pair, plus this package's own README pair and the
* {@link Config.mode} JSDoc. * {@link Config.mode} JSDoc.
*/ */
/**
* Prompt order of the `ptc` collapse statement: after the persona and before
* per-tool guidance, so the model reads which tools it may call before it
* reads what each one is for.
*/
const COLLAPSE_SECTION_ORDER = FIRST_PARTY_SECTION_ORDER.PTC_ONLY
/** /**
* The model-facing statement of the `ptc` collapse. Names the consequence * The model-facing statement of the `ptc` collapse. Names the consequence
* (the call fails) and the route (inside the program), because a rule the * (the call fails) and the route (inside the program), because a rule the
@ -98,7 +91,6 @@ export {
type JsonSchemaScalar, type JsonSchemaScalar,
} from './json-schema.ts' } from './json-schema.ts'
export type { JsonValue } from '@deepseek-ai/dsh-session'
export type { PtcDispatchEventData, PtcDispatchStartEventData } from './types.ts' export type { PtcDispatchEventData, PtcDispatchStartEventData } from './types.ts'
export { CodeRunFailedError, RUN_CODE_NAME } from './ptc.ts' export { CodeRunFailedError, RUN_CODE_NAME } from './ptc.ts'
@ -846,8 +838,8 @@ export class ToolRuntime extends Service {
* Without this the model reads a catalog of tools it is told to use and no * Without this the model reads a catalog of tools it is told to use and no
* statement that only `run_code` may be called, so it emits a native call, * statement that only `run_code` may be called, so it emits a native call,
* receives `UNKNOWN_TOOL` for a tool the prompt just declared, and concludes * receives `UNKNOWN_TOOL` for a tool the prompt just declared, and concludes
* the deployment is inconsistent. {@link COLLAPSE_SECTION_ORDER} places the rule * the deployment is inconsistent. Its order places the rule before that
* before that guidance rather than after it. * guidance rather than after it.
* *
* `both` renders empty: native calls do execute there, so the rule is false. * `both` renders empty: native calls do execute there, so the rule is false.
* @returns the section registration. * @returns the section registration.
@ -855,7 +847,7 @@ export class ToolRuntime extends Service {
private collapseSection(): { name: string; order: number; text: (context: { scope?: ScopeKey }) => string } { private collapseSection(): { name: string; order: number; text: (context: { scope?: ScopeKey }) => string } {
return { return {
name: 'tools:ptc-only', name: 'tools:ptc-only',
order: COLLAPSE_SECTION_ORDER, order: this.ctx.systemPrompt.getSectionOrder('PTC_ONLY'),
// The SAME predicate the executor denies by, so the prompt cannot state // The SAME predicate the executor denies by, so the prompt cannot state
// a rule the registry does not enforce (see `collapses`). // a rule the registry does not enforce (see `collapses`).
text: context => this.modeFor(context.scope) === 'ptc' ? PTC_ONLY_INSTRUCTION : '', text: context => this.modeFor(context.scope) === 'ptc' ? PTC_ONLY_INSTRUCTION : '',
@ -875,7 +867,7 @@ export class ToolRuntime extends Service {
private sdkSection(): { name: string; order: number; text: (context: { scope?: ScopeKey }) => string } { private sdkSection(): { name: string; order: number; text: (context: { scope?: ScopeKey }) => string } {
return { return {
name: 'tools:sdk', name: 'tools:sdk',
order: SDK_SECTION_ORDER, order: this.ctx.systemPrompt.getSectionOrder('TOOLS_SDK'),
// Regenerate from the calling scope's visible tools in stable order. // Regenerate from the calling scope's visible tools in stable order.
text: (context) => { text: (context) => {
const mode = this.modeFor(context.scope) const mode = this.modeFor(context.scope)

View file

@ -11,8 +11,8 @@
* @module dsh-tools/json-schema * @module dsh-tools/json-schema
*/ */
import { assertNever, HarnessError } from '@deepseek-ai/dsh-llm' import { HarnessError } from '@deepseek-ai/dsh-llm'
import { isJsonValue, type JsonValue } from '@deepseek-ai/dsh-session' import { assertNever, isJsonValue, type JsonValue } from '@deepseek-ai/dsh-util-values'
/** Scalar JSON values supported by `enum` and `const`. */ /** Scalar JSON values supported by `enum` and `const`. */
export type JsonSchemaScalar = string | number | boolean | null export type JsonSchemaScalar = string | number | boolean | null

View file

@ -6,12 +6,11 @@
* @module @deepseek-ai/dsh-tools/src/ptc * @module @deepseek-ai/dsh-tools/src/ptc
*/ */
import { ToolCallId, createUserMessage, HarnessError } from '@deepseek-ai/dsh-llm' import { brandString } from '@deepseek-ai/dsh-brand'
import type { ContentBlock } from '@deepseek-ai/dsh-llm' import { createUserMessage, HarnessError } from '@deepseek-ai/dsh-llm'
import type { ContentBlock, ToolCallId } from '@deepseek-ai/dsh-llm'
import type { CodeBindingFunction, CodeRunResult, CodeRuntime } from '@deepseek-ai/dsh-code-runtime' import type { CodeBindingFunction, CodeRunResult, CodeRuntime } from '@deepseek-ai/dsh-code-runtime'
import { snapshotJsonValue } from '@deepseek-ai/dsh-session' import { snapshotJsonValue, type JsonValue } from '@deepseek-ai/dsh-util-values'
import type { JsonValue } from '@deepseek-ai/dsh-session'
import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt'
import { defineTool, parameterSchemaSpecToJsonSchema } from './schema.ts' import { defineTool, parameterSchemaSpecToJsonSchema } from './schema.ts'
import { TOOL_RUNTIME_SCHEDULER } from './index.ts' import { TOOL_RUNTIME_SCHEDULER } from './index.ts'
import type { PtcDispatchLog, ToolDefinition, ToolExecutionResult, ToolRuntime, ToolRunContext } from './index.ts' import type { PtcDispatchLog, ToolDefinition, ToolExecutionResult, ToolRuntime, ToolRunContext } from './index.ts'
@ -20,9 +19,6 @@ import type {} from './types.ts'
/** The model-facing name of the PTC mode tool. */ /** The model-facing name of the PTC mode tool. */
export const RUN_CODE_NAME = 'run_code' export const RUN_CODE_NAME = 'run_code'
/** The `tools:sdk` section order, after per-tool guidance sections. */
export const SDK_SECTION_ORDER = FIRST_PARTY_SECTION_ORDER.TOOLS_SDK
/** /**
* The language-specific `run_code` schema text: the tool `description` and its * The language-specific `run_code` schema text: the tool `description` and its
* `code` parameter description, kept together so a language's two model-facing * `code` parameter description, kept together so a language's two model-facing
@ -470,7 +466,7 @@ export function createRunCodeTool(registry: ToolRuntime, options: RunCodeBridgeO
} }
const normalized = jsonNormalizeArgs(rawArgs) const normalized = jsonNormalizeArgs(rawArgs)
const n = ++dispatches const n = ++dispatches
const subCallId = ToolCallId(`${String(exec.callId)}:code:${n}`) const subCallId = brandString<ToolCallId>(`${String(exec.callId)}:code:${n}`)
const input = { const input = {
callId: subCallId, callId: subCallId,
rootCallId: exec.rootCallId, rootCallId: exec.rootCallId,

View file

@ -2,7 +2,7 @@
import { HarnessError } from '@deepseek-ai/dsh-llm' import { HarnessError } from '@deepseek-ai/dsh-llm'
import type { ContentBlock } from '@deepseek-ai/dsh-llm' import type { ContentBlock } from '@deepseek-ai/dsh-llm'
import type { JsonValue } from '@deepseek-ai/dsh-session' import type { JsonValue } from '@deepseek-ai/dsh-util-values'
import type { ToolDefinition, ToolExecution, ToolExecutionResult, ToolRunContext, ToolResult } from './index.ts' import type { ToolDefinition, ToolExecution, ToolExecutionResult, ToolRunContext, ToolResult } from './index.ts'
import { assertSupportedJsonSchema, isJsonSchemaRecord, isPlainJsonArray, JsonSchemaError, validateJsonSchemaValue } from './json-schema.ts' import { assertSupportedJsonSchema, isJsonSchemaRecord, isPlainJsonArray, JsonSchemaError, validateJsonSchemaValue } from './json-schema.ts'
import type { JsonSchemaNode, JsonSchemaScalar, ObjectJsonSchema } from './json-schema.ts' import type { JsonSchemaNode, JsonSchemaScalar, ObjectJsonSchema } from './json-schema.ts'

View file

@ -1,7 +1,7 @@
/** Canonical tool-definition fixtures for repository tests. @module dsh-tools/testing */ /** Canonical tool-definition fixtures for repository tests. @module dsh-tools/testing */
import type { ContentBlock } from '@deepseek-ai/dsh-llm' import type { ContentBlock } from '@deepseek-ai/dsh-llm'
import type { JsonValue } from '@deepseek-ai/dsh-session' import type { JsonValue } from '@deepseek-ai/dsh-util-values'
import { defineTool } from './schema.ts' import { defineTool } from './schema.ts'
import type { DefineToolOptions, ParameterSchemaSpec } from './schema.ts' import type { DefineToolOptions, ParameterSchemaSpec } from './schema.ts'
import type { ToolDefinition, ToolRunContext } from './index.ts' import type { ToolDefinition, ToolRunContext } from './index.ts'

View file

@ -7,7 +7,7 @@
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import fc from 'fast-check' import fc from 'fast-check'
import { isJsonValue } from '@deepseek-ai/dsh-session' import { isJsonValue } from '@deepseek-ai/dsh-util-values'
import { parameterSchemaSpecToJsonSchema, validateArgs } from '@deepseek-ai/dsh-tools' import { parameterSchemaSpecToJsonSchema, validateArgs } from '@deepseek-ai/dsh-tools'
import type { ParameterPropertySpec, ParameterSchemaSpec, ValueSchemaSpec } from '@deepseek-ai/dsh-tools' import type { ParameterPropertySpec, ParameterSchemaSpec, ValueSchemaSpec } from '@deepseek-ai/dsh-tools'

View file

@ -3,14 +3,15 @@ import { Context } from '@deepseek-ai/cordis'
import { createUserMessage, ToolCallId } from '@deepseek-ai/dsh-llm' import { createUserMessage, ToolCallId } from '@deepseek-ai/dsh-llm'
import { createScope } from '@deepseek-ai/dsh-scope' import { createScope } from '@deepseek-ai/dsh-scope'
import type { Scope } from '@deepseek-ai/dsh-scope' import type { Scope } from '@deepseek-ai/dsh-scope'
import SystemPrompt, { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt' import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import { CodeRuntime } from '@deepseek-ai/dsh-code-runtime' import { CodeRuntime } from '@deepseek-ai/dsh-code-runtime'
import type { CodeRunRequest, CodeRunResult } from '@deepseek-ai/dsh-code-runtime' import type { CodeRunRequest, CodeRunResult } from '@deepseek-ai/dsh-code-runtime'
import ToolRuntime, { CodeRunFailedError, RUN_CODE_NAME, TOOL_ABORTED_BEFORE_DISPATCH, defineContentToolFixture, defineTool } from '@deepseek-ai/dsh-tools' import ToolRuntime, { CodeRunFailedError, RUN_CODE_NAME, TOOL_ABORTED_BEFORE_DISPATCH, defineContentToolFixture, defineTool } from '@deepseek-ai/dsh-tools'
import type { Config, JsonSchemaNode, PostToolDecision, ToolExecutionResult } from '@deepseek-ai/dsh-tools' import type { Config, JsonSchemaNode, PostToolDecision, ToolExecutionResult } from '@deepseek-ai/dsh-tools'
import type { Agent } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent'
import { Session, SessionId } from '@deepseek-ai/dsh-session' import { Session, SessionId } from '@deepseek-ai/dsh-session'
import type { JsonValue, SessionEventMap } from '@deepseek-ai/dsh-session' import type { SessionEventMap } from '@deepseek-ai/dsh-session'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
const testToolSignal = new AbortController().signal const testToolSignal = new AbortController().signal
@ -143,7 +144,7 @@ describe('mode-aware wire contribution', () => {
// saying how it is reached. // saying how it is reached.
ctx.systemPrompt.section({ ctx.systemPrompt.section({
name: 'tool:echo', name: 'tool:echo',
order: FIRST_PARTY_SECTION_ORDER.TOOL_READ, order: ctx.systemPrompt.getSectionOrder('TOOL_READ'),
text: 'Use the echo tool.', text: 'Use the echo tool.',
}) })
@ -213,7 +214,7 @@ describe('mode-aware wire contribution', () => {
const { scope, agent } = await mintAgentScope(ctx) const { scope, agent } = await mintAgentScope(ctx)
scope.ctx.systemPrompt.section({ scope.ctx.systemPrompt.section({
name: 'tools:sdk', name: 'tools:sdk',
order: FIRST_PARTY_SECTION_ORDER.TOOLS_SDK, order: scope.ctx.systemPrompt.getSectionOrder('TOOLS_SDK'),
text: 'SCOPED SDK', text: 'SCOPED SDK',
}) })
@ -301,7 +302,7 @@ describe('mode-aware wire contribution', () => {
expect(() => scope.ctx.tools.restrict({ deny: [RUN_CODE_NAME] })).toThrow(/cannot name reserved PTC mode presentation transport/) expect(() => scope.ctx.tools.restrict({ deny: [RUN_CODE_NAME] })).toThrow(/cannot name reserved PTC mode presentation transport/)
scope.ctx.systemPrompt.section({ scope.ctx.systemPrompt.section({
name: 'scoped-note', name: 'scoped-note',
order: FIRST_PARTY_SECTION_ORDER.TOOLS_SDK - 10, order: scope.ctx.systemPrompt.getSectionOrder('TOOLS_SDK') - 10,
text: 'safe note', text: 'safe note',
}) })
scope.ctx.tools.register(defineContentToolFixture({ scope.ctx.tools.register(defineContentToolFixture({

View file

@ -5,10 +5,10 @@ import {
valueSchemaSpecToJsonSchema, valueSchemaSpecToJsonSchema,
type InferArgs, type InferArgs,
type InferValue, type InferValue,
type JsonValue,
type ParameterSchemaSpec, type ParameterSchemaSpec,
type ValueSchemaSpec, type ValueSchemaSpec,
} from '../src/index.ts' } from '../src/index.ts'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
describe('the unified author schema DSL', () => { describe('the unified author schema DSL', () => {
it('compiles every value root and the author-only json node', () => { it('compiles every value root and the author-only json node', () => {

View file

@ -10,9 +10,10 @@ import ApprovalService, { type ApprovalOutcome, type ApprovalRequest } from '@de
import ToolRuntime, { import ToolRuntime, {
defineContentToolFixture, defineTool, JsonSchemaError, parameterSchemaSpecToJsonSchema, validateArgs, ToolArgsError, ToolNotFoundError, defineContentToolFixture, defineTool, JsonSchemaError, parameterSchemaSpecToJsonSchema, validateArgs, ToolArgsError, ToolNotFoundError,
TOOL_ABORTED, TOOL_ABORTED_BEFORE_DISPATCH, TOOL_ABORTED, TOOL_ABORTED_BEFORE_DISPATCH,
type InferArgs, type JsonValue, type ParameterSchemaSpec, type PreToolDecision, type PostToolDecision, type InferArgs, type ParameterSchemaSpec, type PreToolDecision, type PostToolDecision,
type JsonSchemaNode, type ToolDefinition, type ToolDispatchExecution, type ToolExecutionResult, type ToolExecutionToken, type JsonSchemaNode, type ToolDefinition, type ToolDispatchExecution, type ToolExecutionResult, type ToolExecutionToken,
} from '@deepseek-ai/dsh-tools' } from '@deepseek-ai/dsh-tools'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
const testToolSignal = new AbortController().signal const testToolSignal = new AbortController().signal

View file

@ -9,6 +9,7 @@
*/ */
import { Context, Service } from '@deepseek-ai/cordis' import { Context, Service } from '@deepseek-ai/cordis'
import { brandString } from '@deepseek-ai/dsh-brand'
import type { CredentialInfo, CredentialKey, CredentialRecord, CredentialRef } from './types.ts' import type { CredentialInfo, CredentialKey, CredentialRecord, CredentialRef } from './types.ts'
export type { export type {
@ -29,7 +30,7 @@ export function credentialRef(value: string): CredentialRef {
if (!isCredentialRefName(value)) { if (!isCredentialRefName(value)) {
throw new TypeError(`credential ref "${value}" must match ${String(REF_PATTERN)}`) throw new TypeError(`credential ref "${value}" must match ${String(REF_PATTERN)}`)
} }
return value as CredentialRef return brandString<CredentialRef>(value)
} }
/** /**
@ -71,7 +72,7 @@ export function credentialKey(scope: string, id: string): CredentialKey {
throw new TypeError(`credential key segment "${segment}" must match ${String(KEY_SEGMENT_PATTERN)}`) throw new TypeError(`credential key segment "${segment}" must match ${String(KEY_SEGMENT_PATTERN)}`)
} }
} }
return `${scope}/${id}` as CredentialKey return brandString<CredentialKey>(`${scope}/${id}`)
} }
/** /**

View file

@ -2,11 +2,11 @@
import { randomUUID } from 'node:crypto' import { randomUUID } from 'node:crypto'
import type { Context } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis'
import { brandString } from '@deepseek-ai/dsh-brand'
import type { Agent } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent'
import { createUserMessage } from '@deepseek-ai/dsh-llm' import { createUserMessage } from '@deepseek-ai/dsh-llm'
import type { ContentBlock } from '@deepseek-ai/dsh-llm' import type { ContentBlock } from '@deepseek-ai/dsh-llm'
import { SessionId } from '@deepseek-ai/dsh-session' import type { Session, SessionEvent, SessionId } from '@deepseek-ai/dsh-session'
import type { Session, SessionEvent } from '@deepseek-ai/dsh-session'
import { errorMessage, TeamError } from './error.ts' import { errorMessage, TeamError } from './error.ts'
import type { TeamJournal } from './journal.ts' import type { TeamJournal } from './journal.ts'
import type { TeamRuntimeLifecycle } from './lifecycle.ts' import type { TeamRuntimeLifecycle } from './lifecycle.ts'
@ -68,7 +68,7 @@ export class TeamMailbox {
if (this.lifecycle.disposed || event.type !== 'user/message' || event.data.source.kind !== 'team-message') return if (this.lifecycle.disposed || event.type !== 'user/message' || event.data.source.kind !== 'team-message') return
const source = event.data.source const source = event.data.source
const acknowledgement = Promise.resolve().then(async () => { const acknowledgement = Promise.resolve().then(async () => {
const root = this.ctx.agents.get(SessionId(source.teamId)) const root = this.ctx.agents.get(brandString<SessionId>(source.teamId))
if (root !== undefined) await this.checkpointDelivered(root, session, source.messageId) if (root !== undefined) await this.checkpointDelivered(root, session, source.messageId)
}).catch((error: unknown) => { }).catch((error: unknown) => {
this.ctx.logger.warn(`Team message "${source.messageId}" acknowledgement failed: ${errorMessage(error)}`) this.ctx.logger.warn(`Team message "${source.messageId}" acknowledgement failed: ${errorMessage(error)}`)

View file

@ -1,9 +1,9 @@
/** Host-only Team state projected incrementally from committed Session events. */ /** Host-only Team state projected incrementally from committed Session events. */
import { z } from 'zod' import { z } from 'zod'
import { brandString } from '@deepseek-ai/dsh-brand'
import type { ContentBlock } from '@deepseek-ai/dsh-llm' import type { ContentBlock } from '@deepseek-ai/dsh-llm'
import { SessionId } from '@deepseek-ai/dsh-session' import type { SessionEvent, SessionEventMap, SessionId } from '@deepseek-ai/dsh-session'
import type { SessionEvent, SessionEventMap } from '@deepseek-ai/dsh-session'
import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection' import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection'
import type { import type {
TeamId, TeamId,
@ -21,7 +21,7 @@ import { assertTaskGraphCandidate } from './task-graph.ts'
const nonNegativeSafeInteger = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER) const nonNegativeSafeInteger = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER)
const positiveSafeInteger = nonNegativeSafeInteger.min(1) const positiveSafeInteger = nonNegativeSafeInteger.min(1)
const sessionIdSchema = z.string().min(1).transform(value => SessionId(value)) const sessionIdSchema = z.string().min(1).transform(value => brandString<SessionId>(value))
const teamIdSchema = z.string().min(1).transform(value => toTeamId(value)) const teamIdSchema = z.string().min(1).transform(value => toTeamId(value))
const numericTaskIdPattern = /^task-(\d+)$/u const numericTaskIdPattern = /^task-(\d+)$/u
const teamTaskIdSchema = z.string().min(1).refine((value) => { const teamTaskIdSchema = z.string().min(1).refine((value) => {

View file

@ -2,9 +2,10 @@
import { randomUUID } from 'node:crypto' import { randomUUID } from 'node:crypto'
import type { Context } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis'
import { brandString } from '@deepseek-ai/dsh-brand'
import type { Agent } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent'
import type { MessageId } from '@deepseek-ai/dsh-llm' import type { MessageId } from '@deepseek-ai/dsh-llm'
import { SessionId } from '@deepseek-ai/dsh-session' import type { SessionId } from '@deepseek-ai/dsh-session'
import { foldSubagentDescriptor } from '@deepseek-ai/dsh-subagent' import { foldSubagentDescriptor } from '@deepseek-ai/dsh-subagent'
import type { ContinuableStart } from '@deepseek-ai/dsh-subagent' import type { ContinuableStart } from '@deepseek-ai/dsh-subagent'
import { errorMessage, TeamError } from './error.ts' import { errorMessage, TeamError } from './error.ts'
@ -254,7 +255,7 @@ export class TeamRoster {
const root = membership.root const root = membership.root
const name = this.memberName(request.name) const name = this.memberName(request.name)
const description = requiredText(request.description, 'description', 200) const description = requiredText(request.description, 'description', 200)
const childId = SessionId(randomUUID()) const childId = brandString<SessionId>(randomUUID())
const member: TeamMemberSnapshot = { const member: TeamMemberSnapshot = {
id: childId, id: childId,
name, name,

View file

@ -5,7 +5,6 @@ import z from '@deepseek-ai/schemastery'
import type { Agent } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent'
import { TeamTaskId } from '@deepseek-ai/dsh-experimental-agent-team' import { TeamTaskId } from '@deepseek-ai/dsh-experimental-agent-team'
import type { TeamMemberView } from '@deepseek-ai/dsh-experimental-agent-team' import type { TeamMemberView } from '@deepseek-ai/dsh-experimental-agent-team'
import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt'
import { defineTool } from '@deepseek-ai/dsh-tools' import { defineTool } from '@deepseek-ai/dsh-tools'
import type { InferValue, ValueSchemaSpec } from '@deepseek-ai/dsh-tools' import type { InferValue, ValueSchemaSpec } from '@deepseek-ai/dsh-tools'
@ -164,7 +163,7 @@ function install(agent: Agent, ctx: Context, config: Required<Config>): () => vo
try { try {
register(scoped.systemPrompt.section({ register(scoped.systemPrompt.section({
name: 'team:policy', name: 'team:policy',
order: FIRST_PARTY_SECTION_ORDER.TEAM_POLICY, order: scoped.systemPrompt.getSectionOrder('TEAM_POLICY'),
text: () => { text: () => {
const membership = ctx.agentTeams.membership(agent) const membership = ctx.agentTeams.membership(agent)
return `${POLICY}\n\nYour Team role is ${membership.role}; your Team name is ${membership.name}; Team id is ${membership.id}.` return `${POLICY}\n\nYour Team role is ${membership.role}; your Team name is ${membership.name}; Team id is ${membership.id}.`

View file

@ -12,11 +12,12 @@
import type { Context } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis'
import type { import type {
ApprovalRequestId, CordisDynamicPluginId, DynamicCordisInvokeResult, JsonValue, ApprovalRequestId, CordisDynamicPluginId, DynamicCordisInvokeResult,
DynamicCordisInventoryRow, DynamicCordisInventoryRow,
} from '@deepseek-ai/dsh-api-remotes/client' } from '@deepseek-ai/dsh-api-remotes/client'
import type { ClientModuleSystem } from '@deepseek-ai/dsh-client-modules/client' import type { ClientModuleSystem } from '@deepseek-ai/dsh-client-modules/client'
import type { SlotRegistry } from '@deepseek-ai/dsh-client-ui-renderer/client' import type { SlotRegistry } from '@deepseek-ai/dsh-client-ui-renderer/client'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
// The Client Remote assembly is the one place the two planes meet: it mounts the // The Client Remote assembly is the one place the two planes meet: it mounts the
// `dynamicCordisRunner` namespace and re-exports its payload vocabulary, so this // `dynamicCordisRunner` namespace and re-exports its payload vocabulary, so this
// package names what it sends without importing a Host package. // package names what it sends without importing a Host package.

View file

@ -3,8 +3,9 @@
import type { Context } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis'
import type { import type {
CordisInspectProviderManifest, CordisInspectQueryRequest, CordisInspectQueryResolution, CordisInspectProviderManifest, CordisInspectQueryRequest, CordisInspectQueryResolution,
CordisInspectRequestId, JsonValue, SessionId, CordisInspectRequestId, SessionId,
} from '@deepseek-ai/dsh-api-remotes/client' } from '@deepseek-ai/dsh-api-remotes/client'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
/** Context supplied to a Client inspect provider query. */ /** Context supplied to a Client inspect provider query. */
export interface ClientCordisInspectQueryContext { export interface ClientCordisInspectQueryContext {

View file

@ -1,7 +1,7 @@
/** Built-in Client inspect providers over live Client-owned services. */ /** Built-in Client inspect providers over live Client-owned services. */
import type { Context } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis'
import type { JsonValue } from '@deepseek-ai/dsh-api-remotes/client' import type { JsonValue } from '@deepseek-ai/dsh-util-values'
import type { SlotRegistry } from '@deepseek-ai/dsh-client-ui-renderer/client' import type { SlotRegistry } from '@deepseek-ai/dsh-client-ui-renderer/client'
import type {} from '@deepseek-ai/dsh-client-ui-theme/client' import type {} from '@deepseek-ai/dsh-client-ui-theme/client'
import { queryEventApi, queryServiceApi } from './api-catalog.ts' import { queryEventApi, queryServiceApi } from './api-catalog.ts'

View file

@ -19,7 +19,7 @@ import { scopeOf } from '@deepseek-ai/dsh-scope'
import { assertSupportedJsonSchema, defineTool } from '@deepseek-ai/dsh-tools' import { assertSupportedJsonSchema, defineTool } from '@deepseek-ai/dsh-tools'
import type { ToolDefinition } from '@deepseek-ai/dsh-tools' import type { ToolDefinition } from '@deepseek-ai/dsh-tools'
import type { ContentBlock } from '@deepseek-ai/dsh-llm' import type { ContentBlock } from '@deepseek-ai/dsh-llm'
import type { JsonValue } from '@deepseek-ai/dsh-session' import type { JsonValue } from '@deepseek-ai/dsh-util-values'
const DYNAMIC_TOOL = Symbol('cordis-host-runner.dynamic-tool') const DYNAMIC_TOOL = Symbol('cordis-host-runner.dynamic-tool')
const SCHEMA_TYPES = new Set<unknown>(['string', 'number', 'integer', 'boolean', 'null', 'object', 'array', 'json']) const SCHEMA_TYPES = new Set<unknown>(['string', 'number', 'integer', 'boolean', 'null', 'object', 'array', 'json'])

View file

@ -9,8 +9,8 @@ import type { Fiber } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery' import z from '@deepseek-ai/schemastery'
import type { Agent } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent'
import { createUserMessage } from '@deepseek-ai/dsh-llm' import { createUserMessage } from '@deepseek-ai/dsh-llm'
import type { JsonValue } from '@deepseek-ai/dsh-session/types'
import { TypertRemoteService, Remote } from '@deepseek-ai/dsh-typert-protocol' import { TypertRemoteService, Remote } from '@deepseek-ai/dsh-typert-protocol'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
import { isPlugin, normalizeHandler } from './guard.ts' import { isPlugin, normalizeHandler } from './guard.ts'
import { CordisInspectRegistryService } from './inspect-registry.ts' import { CordisInspectRegistryService } from './inspect-registry.ts'
import { missingServices, startHostHalf } from './lifecycle.ts' import { missingServices, startHostHalf } from './lifecycle.ts'

View file

@ -3,8 +3,7 @@
import { Service } from '@deepseek-ai/cordis' import { Service } from '@deepseek-ai/cordis'
import type { Context } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis'
import type { Agent } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent'
import { snapshotJsonValue } from '@deepseek-ai/dsh-session' import { snapshotJsonValue, type JsonValue } from '@deepseek-ai/dsh-util-values'
import type { JsonValue } from '@deepseek-ai/dsh-session/types'
import { assertSupportedJsonSchema, validateJsonSchemaValue } from '@deepseek-ai/dsh-tools' import { assertSupportedJsonSchema, validateJsonSchemaValue } from '@deepseek-ai/dsh-tools'
import type { JsonSchemaNode } from '@deepseek-ai/dsh-tools' import type { JsonSchemaNode } from '@deepseek-ai/dsh-tools'
import type { import type {

View file

@ -4,7 +4,8 @@
*/ */
import type { Branded } from '@deepseek-ai/dsh-brand' import type { Branded } from '@deepseek-ai/dsh-brand'
import type { JsonValue, SessionId } from '@deepseek-ai/dsh-session/types' import type { SessionId } from '@deepseek-ai/dsh-session/types'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
/** Stable identity of one dynamic plugin instance. */ /** Stable identity of one dynamic plugin instance. */
export type CordisDynamicPluginId = Branded<'CordisDynamicPluginId'> export type CordisDynamicPluginId = Branded<'CordisDynamicPluginId'>

View file

@ -1914,10 +1914,17 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
returns: 'the absolute local document path, or undefined for non-file storage.', returns: 'the absolute local document path, or undefined for non-file storage.',
}, },
{ {
signature: 'register<T>(ns: SettingsNamespace, schema: z<T>, options?: SettingsRegisterOptions<T>): SettingsScope<T>', signature: 'register<const Namespace extends string, T>( ns: Namespace & SettingsNamespaceInput<Namespace>, schema: z<T>, options?: SettingsRegisterOptions<T>, ): SettingsScope<T>',
description: 'Register a namespace schema and receive its owner scope. The registration is an effect on the calling plugin\'s fiber: disposing that fiber removes the namespace and its observers. An invalid stored section fails the registration itself — the earliest point where the schema can judge it.', description: 'Register a namespace schema and receive its owner scope. The registration is an effect on the calling plugin\'s fiber: disposing that fiber removes the namespace and its observers. An invalid stored section fails the registration itself — the earliest point where the schema can judge it.',
parameters: [{ name: 'ns', description: 'unique namespace; duplicate registration fails loud.' }, { name: 'schema', description: 'schemastery schema resolving this namespace\'s value.' }, { name: 'options', description: 'composition `base` layer and effect timing.' }], parameters: [{ name: 'ns', description: 'unique namespace; duplicate registration fails loud.' }, { name: 'schema', description: 'schemastery schema resolving this namespace\'s value.' }, { name: 'options', description: 'composition `base` layer and effect timing.' }],
returns: 'the owner scope for reads, observation, and updates.', returns: 'the owner scope for reads, observation, and updates.',
throws: ['{TypeError} when `ns` is not a lowercase hyphenated identifier.'],
},
{
signature: 'installSection<const Namespace extends string, T>( owner: Context, ns: Namespace & SettingsNamespaceInput<Namespace>, schema: z<T>, entry: T, hooks: SettingsSectionHooks<T>, ): void',
description: 'Attach one optional-settings consumer to this provider. The consumer registers its composition entry as the base layer while this provider is present, then falls back to that entry if the provider detaches.',
parameters: [{ name: 'owner', description: 'consumer context whose unload suppresses fallback work.' }, { name: 'ns', description: 'consumer-owned settings namespace.' }, { name: 'schema', description: 'schema resolving the namespace.' }, { name: 'entry', description: 'composition entry used as the base and fallback value.' }, { name: 'hooks', description: 'source sink, change notification, and optional validation.' }],
throws: ['{TypeError} when `ns` is not a lowercase hyphenated identifier.'],
}, },
{ {
signature: 'describe(options?: SettingsDescribeOptions): SettingsDescriptor[]', signature: 'describe(options?: SettingsDescribeOptions): SettingsDescriptor[]',
@ -1926,25 +1933,29 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
returns: 'one descriptor per registered namespace, in registration order.', returns: 'one descriptor per registered namespace, in registration order.',
}, },
{ {
signature: 'get(ns: SettingsNamespace): unknown', signature: 'get<const Namespace extends string>(ns: Namespace & SettingsNamespaceInput<Namespace>): unknown',
description: 'Read one registered namespace\'s resolved value.', description: 'Read one registered namespace\'s resolved value.',
parameters: [{ name: 'ns', description: 'the namespace to read.' }], parameters: [{ name: 'ns', description: 'the namespace to read.' }],
returns: 'the resolved value, or `undefined` while unregistered.', returns: 'the resolved value, or `undefined` while unregistered.',
throws: ['{TypeError} when `ns` is not a lowercase hyphenated identifier.'],
}, },
{ {
signature: 'async update(ns: SettingsNamespace, patch: object, expectedRevision?: number): Promise<void>', signature: 'async update<const Namespace extends string>( ns: Namespace & SettingsNamespaceInput<Namespace>, patch: object, expectedRevision?: number, ): Promise<void>',
description: 'Merge a patch into one registered namespace\'s user layer, validate the resolved candidate, persist through the provider, then commit and emit. A validation failure rejects before anything is persisted. Writes to one namespace are serialized: concurrent updates apply in call order, each merging over the previous write\'s committed section.', description: 'Merge a patch into one registered namespace\'s user layer, validate the resolved candidate, persist through the provider, then commit and emit. A validation failure rejects before anything is persisted. Writes to one namespace are serialized: concurrent updates apply in call order, each merging over the previous write\'s committed section.',
parameters: [{ name: 'ns', description: 'the registered namespace to update.' }, { name: 'patch', description: 'plain-object patch over the user section.' }, { name: 'expectedRevision', description: 'the descriptor `revision` the caller read; a namespace that moved past it rejects with {@link SettingsConflictError}.' }], parameters: [{ name: 'ns', description: 'the registered namespace to update.' }, { name: 'patch', description: 'plain-object patch over the user section.' }, { name: 'expectedRevision', description: 'the descriptor `revision` the caller read; a namespace that moved past it rejects with {@link SettingsConflictError}.' }],
throws: ['{TypeError} when `ns` is not a lowercase hyphenated identifier.'],
}, },
{ {
signature: 'async replace(ns: SettingsNamespace, section: object, expectedRevision?: number): Promise<void>', signature: 'async replace<const Namespace extends string>( ns: Namespace & SettingsNamespaceInput<Namespace>, section: object, expectedRevision?: number, ): Promise<void>',
description: 'Replace one registered namespace\'s user section wholesale, validate, persist, then commit and emit. Keys absent from `section` fall back to the composition `base` and schema defaults — this is the removal/reset path a merge-only patch cannot express (`replace({})` re-inherits everything).', description: 'Replace one registered namespace\'s user section wholesale, validate, persist, then commit and emit. Keys absent from `section` fall back to the composition `base` and schema defaults — this is the removal/reset path a merge-only patch cannot express (`replace({})` re-inherits everything).',
parameters: [{ name: 'ns', description: 'the registered namespace to replace.' }, { name: 'section', description: 'the complete next user section.' }, { name: 'expectedRevision', description: 'the descriptor `revision` the caller read; a namespace that moved past it rejects with {@link SettingsConflictError}.' }], parameters: [{ name: 'ns', description: 'the registered namespace to replace.' }, { name: 'section', description: 'the complete next user section.' }, { name: 'expectedRevision', description: 'the descriptor `revision` the caller read; a namespace that moved past it rejects with {@link SettingsConflictError}.' }],
throws: ['{TypeError} when `ns` is not a lowercase hyphenated identifier.'],
}, },
{ {
signature: 'async mutate(ns: SettingsNamespace, ops: readonly SettingsPathOp[], expectedRevision?: number): Promise<void>', signature: 'async mutate<const Namespace extends string>( ns: Namespace & SettingsNamespaceInput<Namespace>, ops: readonly SettingsPathOp[], expectedRevision?: number, ): Promise<void>',
description: 'Apply path-addressed edits to one registered namespace\'s user section, validate, persist, then commit and emit. The ops are applied to the section as it stands when the write reaches the front of the queue, so a caller never has to restate fields it did not touch — and, crucially, cannot delete fields it never saw. This is the write path for any caller holding a redacted view; `replace` remains the wholesale reset.', description: 'Apply path-addressed edits to one registered namespace\'s user section, validate, persist, then commit and emit. The ops are applied to the section as it stands when the write reaches the front of the queue, so a caller never has to restate fields it did not touch — and, crucially, cannot delete fields it never saw. This is the write path for any caller holding a redacted view; `replace` remains the wholesale reset.',
parameters: [{ name: 'ns', description: 'the registered namespace to edit.' }, { name: 'ops', description: 'ordered path edits; later ops observe earlier ones.' }, { name: 'expectedRevision', description: 'the descriptor `revision` the caller read; a namespace that moved past it rejects with {@link SettingsConflictError}.' }], parameters: [{ name: 'ns', description: 'the registered namespace to edit.' }, { name: 'ops', description: 'ordered path edits; later ops observe earlier ones.' }, { name: 'expectedRevision', description: 'the descriptor `revision` the caller read; a namespace that moved past it rejects with {@link SettingsConflictError}.' }],
throws: ['{TypeError} when `ns` is not a lowercase hyphenated identifier.'],
}, },
], ],
}, },
@ -2314,6 +2325,18 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
parameters: [{ name: 'section', description: 'the section to register.' }], parameters: [{ name: 'section', description: 'the section to register.' }],
returns: 'the exact Cordis effect disposer.', returns: 'the exact Cordis effect disposer.',
}, },
{
signature: 'getSectionOrder(name: PromptSectionOrderName): number',
description: 'Resolve the centrally owned placement of a repository prompt section.',
parameters: [{ name: 'name', description: 'stable section placement name.' }],
returns: 'the section\'s numeric sort order.',
},
{
signature: 'getContextOrder(name: PromptContextOrderName): number',
description: 'Resolve the centrally owned placement of a repository runtime context.',
parameters: [{ name: 'name', description: 'stable context placement name.' }],
returns: 'the context\'s numeric sort order.',
},
{ {
signature: 'context(context: PromptContext): () => void', signature: 'context(context: PromptContext): () => void',
description: 'Register ordered dynamic context in the calling context\'s scope. Scoped entries shadow global entries with the same name.', description: 'Register ordered dynamic context in the calling context\'s scope. Scoped entries shadow global entries with the same name.',
@ -4566,10 +4589,18 @@ export const TYPE_API: readonly TypeApiEntry[] = [
name: 'PromptContext', name: 'PromptContext',
declaration: 'export interface PromptContext {\n readonly name: string;\n readonly order: number;\n readonly text: string | ((context: AssembleContext) => string);\n}', declaration: 'export interface PromptContext {\n readonly name: string;\n readonly order: number;\n readonly text: string | ((context: AssembleContext) => string);\n}',
}, },
{
name: 'PromptContextOrderName',
declaration: 'export type PromptContextOrderName = keyof typeof CONTEXT_ORDERS;',
},
{ {
name: 'PromptSection', name: 'PromptSection',
declaration: 'export interface PromptSection {\n readonly name: string;\n readonly order: number;\n readonly text: string | ((context: AssembleContext) => string);\n readonly complete?: boolean;\n}', declaration: 'export interface PromptSection {\n readonly name: string;\n readonly order: number;\n readonly text: string | ((context: AssembleContext) => string);\n readonly complete?: boolean;\n}',
}, },
{
name: 'PromptSectionOrderName',
declaration: 'export type PromptSectionOrderName = keyof typeof SECTION_ORDERS;',
},
{ {
name: 'ProviderRequestId', name: 'ProviderRequestId',
declaration: 'export type ProviderRequestId = Branded<\'ProviderRequestId\'>;', declaration: 'export type ProviderRequestId = Branded<\'ProviderRequestId\'>;',
@ -5214,6 +5245,10 @@ export const TYPE_API: readonly TypeApiEntry[] = [
name: 'SettingsSecretView', name: 'SettingsSecretView',
declaration: 'export interface SettingsSecretView {\n path: string[];\n set: boolean;\n}', declaration: 'export interface SettingsSecretView {\n path: string[];\n set: boolean;\n}',
}, },
{
name: 'SettingsSectionHooks',
declaration: 'export interface SettingsSectionHooks<T> {\n setSource(current: () => T): void;\n onChange(): void;\n validate?: (value: T) => void;\n}',
},
{ {
name: 'SettingsUpdateSource', name: 'SettingsUpdateSource',
declaration: 'export type SettingsUpdateSource = \'update\' | \'provider\';', declaration: 'export type SettingsUpdateSource = \'update\' | \'provider\';',
@ -5520,7 +5555,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
}, },
{ {
name: 'SystemPrompt', name: 'SystemPrompt',
declaration: 'export class SystemPrompt extends Service {\n static Config: z<Config>;\n constructor(ctx: Context, config: Config);\n section(section: PromptSection): () => void;\n context(context: PromptContext): () => void;\n suppressRuntimeContext(): () => void;\n tools(provider: (context: AssembleContext) => ToolProviderResult): () => void;\n variable(name: string, provider: (context: AssembleContext) => string | undefined): () => void;\n async assemble(context: AssembleContext = {}): Promise<PromptAssembly>;\n}', declaration: 'export class SystemPrompt extends Service {\n static Config: z<Config>;\n constructor(ctx: Context, config: Config);\n section(section: PromptSection): () => void;\n getSectionOrder(name: PromptSectionOrderName): number;\n getContextOrder(name: PromptContextOrderName): number;\n context(context: PromptContext): () => void;\n suppressRuntimeContext(): () => void;\n tools(provider: (context: AssembleContext) => ToolProviderResult): () => void;\n variable(name: string, provider: (context: AssembleContext) => string | undefined): () => void;\n async assemble(context: AssembleContext = {}): Promise<PromptAssembly>;\n}',
}, },
{ {
name: 'TableKeyOf', name: 'TableKeyOf',

View file

@ -10,11 +10,10 @@ import {
} from '@deepseek-ai/dsh-cordis-host-runner' } from '@deepseek-ai/dsh-cordis-host-runner'
import type { DynamicCordisReference } from '@deepseek-ai/dsh-cordis-host-runner' import type { DynamicCordisReference } from '@deepseek-ai/dsh-cordis-host-runner'
import { createUserMessage } from '@deepseek-ai/dsh-llm' import { createUserMessage } from '@deepseek-ai/dsh-llm'
import type { JsonValue } from '@deepseek-ai/dsh-session' import type { JsonValue } from '@deepseek-ai/dsh-util-values'
import type { UserMessage } from '@deepseek-ai/dsh-session' import type { UserMessage } from '@deepseek-ai/dsh-session'
import { defineTool } from '@deepseek-ai/dsh-tools' import { defineTool } from '@deepseek-ai/dsh-tools'
import type { ToolExecution } from '@deepseek-ai/dsh-tools' import type { ToolExecution } from '@deepseek-ai/dsh-tools'
import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt'
import { missingServices, providedServices } from './inspect.ts' import { missingServices, providedServices } from './inspect.ts'
import { import {
presentDefineCall, presentInspectListCall, presentInspectQueryCall, presentInspectSelfCall, presentRunCall, presentDefineCall, presentInspectListCall, presentInspectQueryCall, presentInspectSelfCall, presentRunCall,
@ -35,7 +34,7 @@ function requireAgent(exec: ToolExecution): Agent {
export function apply(ctx: Context): void { export function apply(ctx: Context): void {
ctx.systemPrompt.section({ ctx.systemPrompt.section({
name: 'tool:cordis', name: 'tool:cordis',
order: FIRST_PARTY_SECTION_ORDER.TOOL_CORDIS, order: ctx.systemPrompt.getSectionOrder('TOOL_CORDIS'),
text: CORDIS_SYSTEM_PROMPT, text: CORDIS_SYSTEM_PROMPT,
}) })
for (const provider of hostInspectProviders(ctx)) { for (const provider of hostInspectProviders(ctx)) {

View file

@ -3,7 +3,7 @@
import type { Context } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis'
import { HOST_BUILTIN_INSPECTION } from '@deepseek-ai/dsh-cordis-host-runner' import { HOST_BUILTIN_INSPECTION } from '@deepseek-ai/dsh-cordis-host-runner'
import type { HostCordisInspectProviderRegistration } from '@deepseek-ai/dsh-cordis-host-runner' import type { HostCordisInspectProviderRegistration } from '@deepseek-ai/dsh-cordis-host-runner'
import type { JsonValue } from '@deepseek-ai/dsh-session' import type { JsonValue } from '@deepseek-ai/dsh-util-values'
import { EVENT_API, queryEventApi, queryServiceApi } from './api-catalog.ts' import { EVENT_API, queryEventApi, queryServiceApi } from './api-catalog.ts'
const EMPTY_INPUT = { type: 'object', properties: {}, additionalProperties: false } as const const EMPTY_INPUT = { type: 'object', properties: {}, additionalProperties: false } as const

View file

@ -1,7 +1,8 @@
/** Shared top-level-call post-policy selection for search result spill. @module dsh-tool-fs-search/direct-call */ /** Shared top-level-call post-policy selection for search result spill. @module dsh-tool-fs-search/direct-call */
import type { Context } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis'
import type { JsonValue, PostToolDecision, ToolDefinition, ToolExecution, ToolExecutionResult } from '@deepseek-ai/dsh-tools' import type { PostToolDecision, ToolDefinition, ToolExecution, ToolExecutionResult } from '@deepseek-ai/dsh-tools'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
/** /**
* Return the accepted canonical value only when this tool still owns a direct * Return the accepted canonical value only when this tool still owns a direct

View file

@ -14,7 +14,6 @@ import { sep } from 'node:path'
import { defineTool } from '@deepseek-ai/dsh-tools' import { defineTool } from '@deepseek-ai/dsh-tools'
import type { GenericCallView, SearchResultView, ToolResult } from '@deepseek-ai/dsh-tools' import type { GenericCallView, SearchResultView, ToolResult } from '@deepseek-ai/dsh-tools'
import type { SpillRef } from '@deepseek-ai/dsh-spill' import type { SpillRef } from '@deepseek-ai/dsh-spill'
import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt'
import { runRipgrep, toWorkdirRelative, trySaveFormattedResult } from './search-core.ts' import { runRipgrep, toWorkdirRelative, trySaveFormattedResult } from './search-core.ts'
import { globSearchMeta, searchViewFromMeta } from './presentation.ts' import { globSearchMeta, searchViewFromMeta } from './presentation.ts'
import { acceptedDirectCallValue } from './direct-call.ts' import { acceptedDirectCallValue } from './direct-call.ts'
@ -300,7 +299,7 @@ export function applyGlobTool(ctx: Context, caps: GlobToolCaps): void {
: 'while a larger one keeps the modification-time-ordered head.' : 'while a larger one keeps the modification-time-ordered head.'
ctx.systemPrompt.section({ ctx.systemPrompt.section({
name: 'tool:glob', name: 'tool:glob',
order: FIRST_PARTY_SECTION_ORDER.TOOL_GLOB, order: ctx.systemPrompt.getSectionOrder('TOOL_GLOB'),
text: 'Use the glob tool — not shell find — to discover files by path pattern. A pattern with no "/" matches basenames at any depth, so "*" matches every file in the tree rather than its top level. ' text: 'Use the glob tool — not shell find — to discover files by path pattern. A pattern with no "/" matches basenames at any depth, so "*" matches every file in the tree rather than its top level. '
+ `Results are files only, never directories, and include hidden and ignored files: a result that fits comes back in modification-time order, ${overCapGuidance}`, + `Results are files only, never directories, and include hidden and ignored files: a result that fits comes back in modification-time order, ${overCapGuidance}`,
}) })

View file

@ -16,7 +16,6 @@ import { defineTool } from '@deepseek-ai/dsh-tools'
import type { GenericCallView, SearchResultView, ToolResult } from '@deepseek-ai/dsh-tools' import type { GenericCallView, SearchResultView, ToolResult } from '@deepseek-ai/dsh-tools'
import type { RetainedItems } from '@deepseek-ai/dsh-output-retention' import type { RetainedItems } from '@deepseek-ai/dsh-output-retention'
import type { SpillRef } from '@deepseek-ai/dsh-spill' import type { SpillRef } from '@deepseek-ai/dsh-spill'
import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt'
import type { GrepMatch } from './search-core.ts' import type { GrepMatch } from './search-core.ts'
import { SearchError, previewLine, retainGrepMatches, runRipgrep, toWorkdirRelative, trySaveFormattedResult } from './search-core.ts' import { SearchError, previewLine, retainGrepMatches, runRipgrep, toWorkdirRelative, trySaveFormattedResult } from './search-core.ts'
import { grepSearchMeta, searchViewFromMeta } from './presentation.ts' import { grepSearchMeta, searchViewFromMeta } from './presentation.ts'
@ -275,7 +274,7 @@ export function presentGrepResult(
export function applyGrepTool(ctx: Context, caps: GrepToolCaps): void { export function applyGrepTool(ctx: Context, caps: GrepToolCaps): void {
ctx.systemPrompt.section({ ctx.systemPrompt.section({
name: 'tool:grep', name: 'tool:grep',
order: FIRST_PARTY_SECTION_ORDER.TOOL_GREP, order: ctx.systemPrompt.getSectionOrder('TOOL_GREP'),
text: 'Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context.', text: 'Use the grep tool — not shell grep or rg — to search file contents. Use read on a matched file when you need surrounding context.',
}) })

View file

@ -9,7 +9,7 @@
*/ */
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import type { JsonValue } from '@deepseek-ai/dsh-session' import type { JsonValue } from '@deepseek-ai/dsh-util-values'
import { import {
globSearchMeta, globSearchMeta,
grepSearchMeta, grepSearchMeta,

View file

@ -9,7 +9,6 @@ import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools' import { defineTool } from '@deepseek-ai/dsh-tools'
import type { DiffCallView, DiffResultView, ToolResult } from '@deepseek-ai/dsh-tools' import type { DiffCallView, DiffResultView, ToolResult } from '@deepseek-ai/dsh-tools'
import type {} from '@deepseek-ai/dsh-fs' import type {} from '@deepseek-ai/dsh-fs'
import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt'
import { computeHunkDiffs, diffsFromMeta } from './diff.ts' import { computeHunkDiffs, diffsFromMeta } from './diff.ts'
import { remediateFsError } from './error.ts' import { remediateFsError } from './error.ts'
import { sessionResolveOptions } from './session-cwd.ts' import { sessionResolveOptions } from './session-cwd.ts'
@ -76,7 +75,7 @@ export function formatEditOutput(displayPath: string, replaceAll: boolean): stri
export function applyEditTool(ctx: Context, sandbox: FsSandboxController): void { export function applyEditTool(ctx: Context, sandbox: FsSandboxController): void {
ctx.systemPrompt.section({ ctx.systemPrompt.section({
name: 'tool:edit', name: 'tool:edit',
order: FIRST_PARTY_SECTION_ORDER.TOOL_EDIT, order: ctx.systemPrompt.getSectionOrder('TOOL_EDIT'),
text: 'Use the edit tool for targeted changes to existing UTF-8 text files. It replaces literal old_string with new_string; by default old_string must appear exactly once. If old_string appears multiple times, provide a more specific old_string or set replace_all to true. Read the file first (the default fs-observation-policy requires it), unless you just created or edited it in this session.', text: 'Use the edit tool for targeted changes to existing UTF-8 text files. It replaces literal old_string with new_string; by default old_string must appear exactly once. If old_string appears multiple times, provide a more specific old_string or set replace_all to true. Read the file first (the default fs-observation-policy requires it), unless you just created or edited it in this session.',
}) })

View file

@ -8,7 +8,6 @@ import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools' import { defineTool } from '@deepseek-ai/dsh-tools'
import type { GenericCallView, ReadResultView, ToolResult } from '@deepseek-ai/dsh-tools' import type { GenericCallView, ReadResultView, ToolResult } from '@deepseek-ai/dsh-tools'
import type {} from '@deepseek-ai/dsh-fs' import type {} from '@deepseek-ai/dsh-fs'
import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt'
import { buildWindow, formatReadOutput, langFromPath, readMetaFromMeta } from './read-render.ts' import { buildWindow, formatReadOutput, langFromPath, readMetaFromMeta } from './read-render.ts'
import { resolveRegularReadTarget } from './read-target.ts' import { resolveRegularReadTarget } from './read-target.ts'
@ -69,7 +68,7 @@ export function parseReadArgs(args: { file_path: string; offset?: number; limit?
export function applyReadTool(ctx: Context, caps: ReadToolCaps): void { export function applyReadTool(ctx: Context, caps: ReadToolCaps): void {
ctx.systemPrompt.section({ ctx.systemPrompt.section({
name: 'tool:read', name: 'tool:read',
order: FIRST_PARTY_SECTION_ORDER.TOOL_READ, order: ctx.systemPrompt.getSectionOrder('TOOL_READ'),
text: 'Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files.', text: 'Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files.',
}) })

View file

@ -10,7 +10,6 @@ import { defineTool } from '@deepseek-ai/dsh-tools'
import type { DiffCallView, DiffResultView, ToolResult } from '@deepseek-ai/dsh-tools' import type { DiffCallView, DiffResultView, ToolResult } from '@deepseek-ai/dsh-tools'
import type { FsWriteOutcome } from '@deepseek-ai/dsh-fs' import type { FsWriteOutcome } from '@deepseek-ai/dsh-fs'
import type {} from '@deepseek-ai/dsh-fs' import type {} from '@deepseek-ai/dsh-fs'
import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt'
import { computeHunkDiffs, diffsFromMeta } from './diff.ts' import { computeHunkDiffs, diffsFromMeta } from './diff.ts'
import { remediateFsError } from './error.ts' import { remediateFsError } from './error.ts'
import { sessionResolveOptions } from './session-cwd.ts' import { sessionResolveOptions } from './session-cwd.ts'
@ -62,7 +61,7 @@ interface WriteToolArgs {
export function applyWriteTool(ctx: Context, sandbox: FsSandboxController): void { export function applyWriteTool(ctx: Context, sandbox: FsSandboxController): void {
ctx.systemPrompt.section({ ctx.systemPrompt.section({
name: 'tool:write', name: 'tool:write',
order: FIRST_PARTY_SECTION_ORDER.TOOL_WRITE, order: ctx.systemPrompt.getSectionOrder('TOOL_WRITE'),
text: 'Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes.', text: 'Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-observation-policy requires it) and prefer edit for targeted changes.',
}) })

View file

@ -7,7 +7,7 @@
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import { computeHunkDiffs, diffsFromMeta, DIFF_CONTEXT } from '../src/diff.ts' import { computeHunkDiffs, diffsFromMeta, DIFF_CONTEXT } from '../src/diff.ts'
import type { JsonValue } from '@deepseek-ai/dsh-session' import type { JsonValue } from '@deepseek-ai/dsh-util-values'
const lines = (n: number): string => Array.from({ length: n }, (_, i) => `line${i + 1}`).join('\n') + '\n' const lines = (n: number): string => Array.from({ length: n }, (_, i) => `line${i + 1}`).join('\n') + '\n'

View file

@ -11,7 +11,6 @@ import type { GoalRef, GoalView } from '@deepseek-ai/dsh-goal'
import { boundContextSummary, createUserMessage, HarnessError } from '@deepseek-ai/dsh-llm' import { boundContextSummary, createUserMessage, HarnessError } from '@deepseek-ai/dsh-llm'
import { defineTool } from '@deepseek-ai/dsh-tools' import { defineTool } from '@deepseek-ai/dsh-tools'
import type { GenericCallView } from '@deepseek-ai/dsh-tools' import type { GenericCallView } from '@deepseek-ai/dsh-tools'
import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt'
import { import {
completionAuthority, completionAuthority,
goalToolExecution, goalToolExecution,
@ -188,7 +187,7 @@ export function apply(ctx: Context, config: Config): void {
const resolved = resolveConfig(config) const resolved = resolveConfig(config)
ctx.systemPrompt.section({ ctx.systemPrompt.section({
name: 'tool:goal', name: 'tool:goal',
order: FIRST_PARTY_SECTION_ORDER.TOOL_GOAL, order: ctx.systemPrompt.getSectionOrder('TOOL_GOAL'),
text: guidance(resolved.blockedAfterConsecutiveRounds), text: guidance(resolved.blockedAfterConsecutiveRounds),
}) })

View file

@ -21,7 +21,7 @@ import { SANDBOX_MODES, setSandboxMode } from '@deepseek-ai/dsh-sandbox-policy'
import type {} from '@deepseek-ai/dsh-shell' import type {} from '@deepseek-ai/dsh-shell'
import type { ApprovalPolicy } from '@deepseek-ai/dsh-user-approval' import type { ApprovalPolicy } from '@deepseek-ai/dsh-user-approval'
import { APPROVAL_POLICIES, setApprovalPolicy } from '@deepseek-ai/dsh-user-approval' import { APPROVAL_POLICIES, setApprovalPolicy } from '@deepseek-ai/dsh-user-approval'
import { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings' import type {} from '@deepseek-ai/dsh-settings'
// Type-only: resolves the optional projection and command children. // Type-only: resolves the optional projection and command children.
import type {} from '@deepseek-ai/dsh-session-projection' import type {} from '@deepseek-ai/dsh-session-projection'
import type {} from '@deepseek-ai/dsh-commands' import type {} from '@deepseek-ai/dsh-commands'
@ -73,7 +73,7 @@ export interface PresetSpec {
export const CUSTOM_PRESET = 'custom' export const CUSTOM_PRESET = 'custom'
/** Settings namespace carrying the default for future sessions. */ /** Settings namespace carrying the default for future sessions. */
export const PERMISSION_SETTINGS_NAMESPACE = settingsNamespace('permission') export const PERMISSION_SETTINGS_NAMESPACE = 'permission'
/** /**
* The projection unit's knob state: the last seen value of each knob event, * The projection unit's knob state: the last seen value of each knob event,
@ -211,13 +211,15 @@ export class PermissionPresetService extends Service {
const settingsSchema: z<PermissionSettings> = z.object({ const settingsSchema: z<PermissionSettings> = z.object({
defaultPreset: z.union(presetChoices).required(), defaultPreset: z.union(presetChoices).required(),
}) })
installSettingsSection(ctx, PERMISSION_SETTINGS_NAMESPACE, settingsSchema, baseSettings, { ctx.inject(['settings'], (settingsCtx) => {
setSource: (current) => { settingsCtx.settings.installSection(ctx, PERMISSION_SETTINGS_NAMESPACE, settingsSchema, baseSettings, {
this.defaultSettings = current setSource: (current) => {
}, this.defaultSettings = current
// The source thunk reads the latest scope snapshot at session creation; },
// no process-level registration needs replacement on change. // The source thunk reads the latest scope snapshot at session creation;
onChange: () => {}, // no process-level registration needs replacement on change.
onChange: () => {},
})
}) })
// zod `.optional()` types the key `string | undefined` while the domain // zod `.optional()` types the key `string | undefined` while the domain

View file

@ -169,7 +169,7 @@ export class ApprovalService extends Service {
ctx.inject(['systemPrompt'], (scope: Context) => { ctx.inject(['systemPrompt'], (scope: Context) => {
scope.systemPrompt.context({ scope.systemPrompt.context({
name: 'approval:policy', name: 'approval:policy',
order: 115, order: scope.systemPrompt.getContextOrder('APPROVAL_POLICY'),
text: (context) => { text: (context) => {
const agent = context.agent const agent = context.agent
// A bare assemble() (tests, diagnostics) has no session to state. // A bare assemble() (tests, diagnostics) has no session to state.

View file

@ -15,7 +15,6 @@ import { defineTool } from '@deepseek-ai/dsh-tools'
import type { GenericCallView, ToolDefinition, ToolExecution } from '@deepseek-ai/dsh-tools' import type { GenericCallView, ToolDefinition, ToolExecution } from '@deepseek-ai/dsh-tools'
import { JobId } from '@deepseek-ai/dsh-jobs' import { JobId } from '@deepseek-ai/dsh-jobs'
import type { JobSnapshot } from '@deepseek-ai/dsh-jobs' import type { JobSnapshot } from '@deepseek-ai/dsh-jobs'
import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt'
import type { Agent } from '@deepseek-ai/dsh-agent' import type { Agent } from '@deepseek-ai/dsh-agent'
export const name = 'tool-jobs' export const name = 'tool-jobs'
@ -262,7 +261,7 @@ export function apply(ctx: Context, config: Config): void {
// Cross-call guidance follows the filesystem sections and precedes product sections. // Cross-call guidance follows the filesystem sections and precedes product sections.
ctx.systemPrompt.section({ ctx.systemPrompt.section({
name: 'tool:jobs', name: 'tool:jobs',
order: FIRST_PARTY_SECTION_ORDER.TOOL_JOBS, order: ctx.systemPrompt.getSectionOrder('TOOL_JOBS'),
text: 'Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job\'s work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering.', text: 'Track every background job id you start. You are notified in-session when a job finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running job\'s work. Before giving a final answer, collect every still-relevant job with job_output (set wait: true only when you are genuinely blocked on it), and job_kill jobs that stopped mattering.',
}) })

View file

@ -18,8 +18,9 @@ import type { ModelModality, RetryPolicyConfig } from '@deepseek-ai/dsh-llm'
import type {} from '@deepseek-ai/dsh-fs' import type {} from '@deepseek-ai/dsh-fs'
import { credentialRef } from '@deepseek-ai/dsh-credentials' import { credentialRef } from '@deepseek-ai/dsh-credentials'
import { launchEnvironmentOf, type LaunchEnvironmentSnapshot } from '@deepseek-ai/dsh-launch-environment' import { launchEnvironmentOf, type LaunchEnvironmentSnapshot } from '@deepseek-ai/dsh-launch-environment'
import { deepEqualJson, installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings' import type {} from '@deepseek-ai/dsh-settings'
import { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout' import { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout'
import { deepEqualJson } from '@deepseek-ai/dsh-util-values'
import { getOrCreateAnonymousUserId, type AnonymousUserId } from '@deepseek-ai/dsh-anonymous-user-id' import { getOrCreateAnonymousUserId, type AnonymousUserId } from '@deepseek-ai/dsh-anonymous-user-id'
import { import {
DEFAULT_CONTEXT_WINDOW, DEFAULT_CONTEXT_WINDOW,
@ -83,7 +84,7 @@ export type * from './types.ts'
export const name = 'llm-deepseek' export const name = 'llm-deepseek'
export const inject = ['llm'] export const inject = ['llm']
const NS = settingsNamespace('llm-deepseek') const NS = 'llm-deepseek'
const DEFAULT_API_KEY_ENV = 'DEEPSEEK_API_KEY' const DEFAULT_API_KEY_ENV = 'DEEPSEEK_API_KEY'
/** The single provider route this plugin owns. */ /** The single provider route this plugin owns. */
const PROVIDER = 'deepseek-official' const PROVIDER = 'deepseek-official'
@ -486,10 +487,12 @@ export function apply(ctx: Context, config: Config): void {
registeredPolicy = policy registeredPolicy = policy
} }
installSettingsSection(ctx, NS, Config, config, { ctx.inject(['settings'], (settingsCtx) => {
setSource: (source) => { settingsCtx.settings.installSection(ctx, NS, Config, config, {
current = source setSource: (source) => {
}, current = source
onChange: ensureRegistrationFacts, },
onChange: ensureRegistrationFacts,
})
}) })
} }

View file

@ -8,8 +8,9 @@
* @module dsh-llm-deepseek/translate * @module dsh-llm-deepseek/translate
*/ */
import { ToolCallId, EMPTY_RESPONSE_CODE, LlmError } from '@deepseek-ai/dsh-llm' import { brandString } from '@deepseek-ai/dsh-brand'
import type { ContentBlock, FinishReason, StreamChunk, TokenUsage } from '@deepseek-ai/dsh-llm' import { EMPTY_RESPONSE_CODE, LlmError } from '@deepseek-ai/dsh-llm'
import type { ContentBlock, FinishReason, StreamChunk, TokenUsage, ToolCallId } from '@deepseek-ai/dsh-llm'
import { DONE } from './sse.ts' import { DONE } from './sse.ts'
import type { WireChunk, WireUsage } from './types.ts' import type { WireChunk, WireUsage } from './types.ts'
@ -77,7 +78,7 @@ function closeBlock(block: OpenBlock): ContentBlock {
case 'reasoning': return { type: 'reasoning', text: block.text } case 'reasoning': return { type: 'reasoning', text: block.text }
case 'tool-call': return { case 'tool-call': return {
type: 'tool-call', type: 'tool-call',
id: ToolCallId(block.callId ?? ''), id: brandString<ToolCallId>(block.callId ?? ''),
name: block.name ?? '', name: block.name ?? '',
arguments: block.text, arguments: block.text,
} }
@ -172,7 +173,7 @@ export async function* translate(payloads: AsyncIterable<string>): AsyncGenerato
yield { yield {
type: 'tool-call-delta', type: 'tool-call-delta',
index: block.index, index: block.index,
id: ToolCallId(block.callId ?? ''), id: brandString<ToolCallId>(block.callId ?? ''),
...block.name !== undefined ? { name: block.name } : {}, ...block.name !== undefined ? { name: block.name } : {},
argumentsDelta: fragment, argumentsDelta: fragment,
} }

View file

@ -15,13 +15,12 @@ import type {
} from '@deepseek-ai/dsh-attachment' } from '@deepseek-ai/dsh-attachment'
import { credentialRef } from '@deepseek-ai/dsh-credentials' import { credentialRef } from '@deepseek-ai/dsh-credentials'
import { LocalCredentialProvider } from '@deepseek-ai/dsh-credentials-local' import { LocalCredentialProvider } from '@deepseek-ai/dsh-credentials-local'
import { settingsNamespace } from '@deepseek-ai/dsh-settings'
import { FileSettingsProvider } from '@deepseek-ai/dsh-settings-file' import { FileSettingsProvider } from '@deepseek-ai/dsh-settings-file'
import * as LlmDeepSeek from '@deepseek-ai/dsh-llm-deepseek' import * as LlmDeepSeek from '@deepseek-ai/dsh-llm-deepseek'
import { assemble } from './assemble.ts' import { assemble } from './assemble.ts'
import { closeMockServers, mockServer, textEvents } from './mock-server.ts' import { closeMockServers, mockServer, textEvents } from './mock-server.ts'
const NS = settingsNamespace('llm-deepseek') const NS = 'llm-deepseek'
const KEY_REF = credentialRef('DEEPSEEK_API_KEY') const KEY_REF = credentialRef('DEEPSEEK_API_KEY')
const IMAGE_REF: ImageAttachmentRef = { const IMAGE_REF: ImageAttachmentRef = {
attachmentId: AttachmentId(`sha256:${'a'.repeat(64)}`), attachmentId: AttachmentId(`sha256:${'a'.repeat(64)}`),

View file

@ -21,7 +21,6 @@ import AgentRegistry from '@deepseek-ai/dsh-agent'
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session' import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
import { credentialRef } from '@deepseek-ai/dsh-credentials' import { credentialRef } from '@deepseek-ai/dsh-credentials'
import LocalCredentialProvider from '@deepseek-ai/dsh-credentials-local' import LocalCredentialProvider from '@deepseek-ai/dsh-credentials-local'
import { settingsNamespace } from '@deepseek-ai/dsh-settings'
import FileSettingsProvider from '@deepseek-ai/dsh-settings-file' import FileSettingsProvider from '@deepseek-ai/dsh-settings-file'
import { getOrCreateAnonymousUserId } from '@deepseek-ai/dsh-anonymous-user-id' import { getOrCreateAnonymousUserId } from '@deepseek-ai/dsh-anonymous-user-id'
import DeepSeekLlmApiExtensionRegistry from '@deepseek-ai/dsh-deepseek-llm-api-extensions' import DeepSeekLlmApiExtensionRegistry from '@deepseek-ai/dsh-deepseek-llm-api-extensions'
@ -31,7 +30,7 @@ import * as LlmDeepSeek from '@deepseek-ai/dsh-llm-deepseek'
import { assemble } from './assemble.ts' import { assemble } from './assemble.ts'
import { closeMockServers, mockServer, textEvents } from './mock-server.ts' import { closeMockServers, mockServer, textEvents } from './mock-server.ts'
const NS = settingsNamespace('llm-deepseek') const NS = 'llm-deepseek'
const KEY_REF = credentialRef('DEEPSEEK_API_KEY') const KEY_REF = credentialRef('DEEPSEEK_API_KEY')
let root: string | undefined let root: string | undefined

View file

@ -4,8 +4,9 @@
* @module dsh-llm-pi-ai/context * @module dsh-llm-pi-ai/context
*/ */
import { ToolCallId, contentHasImage, LlmError, offloadedImageText, offloadRequestImagesWithPolicy, requestImageHandleText } from '@deepseek-ai/dsh-llm' import { brandString } from '@deepseek-ai/dsh-brand'
import type { ContentBlock, GenerateOptions, ImageAttachmentAccessResolver, Message } from '@deepseek-ai/dsh-llm' import { contentHasImage, LlmError, offloadedImageText, offloadRequestImagesWithPolicy, requestImageHandleText } from '@deepseek-ai/dsh-llm'
import type { ContentBlock, GenerateOptions, ImageAttachmentAccessResolver, Message, ToolCallId } from '@deepseek-ai/dsh-llm'
import type { import type {
AttachmentId, AttachmentId,
AttachmentStore, AttachmentStore,
@ -137,6 +138,19 @@ function piContext(options: GenerateOptions, messages: PiMessage[]): PiContext {
} }
} }
function appendAssistant(
message: Message,
messages: PiMessage[],
toolNames: Map<ToolCallId, string>,
onReplayDegrade?: (reason: string) => void,
): void {
const assistant = toPiAssistant(message, onReplayDegrade)
for (const block of assistant.content) {
if (block.type === 'toolCall') toolNames.set(brandString<ToolCallId>(block.id), block.name)
}
messages.push(assistant)
}
function textOnlyContext(options: GenerateOptions, onReplayDegrade?: (reason: string) => void): PiContext { function textOnlyContext(options: GenerateOptions, onReplayDegrade?: (reason: string) => void): PiContext {
const toolNames = new Map<ToolCallId, string>() const toolNames = new Map<ToolCallId, string>()
const messages: PiMessage[] = [] const messages: PiMessage[] = []
@ -149,9 +163,7 @@ function textOnlyContext(options: GenerateOptions, onReplayDegrade?: (reason: st
continue continue
} }
if (message.role === 'assistant') { if (message.role === 'assistant') {
const assistant = toPiAssistant(message, onReplayDegrade) appendAssistant(message, messages, toolNames, onReplayDegrade)
for (const block of assistant.content) if (block.type === 'toolCall') toolNames.set(ToolCallId(block.id), block.name)
messages.push(assistant)
continue continue
} }
const text = flattenText(message) const text = flattenText(message)
@ -263,11 +275,7 @@ async function toPiContextWithImages(
continue continue
} }
if (message.role === 'assistant') { if (message.role === 'assistant') {
const assistant = toPiAssistant(message, onReplayDegrade) appendAssistant(message, messages, toolNames, onReplayDegrade)
for (const block of assistant.content) {
if (block.type === 'toolCall') toolNames.set(ToolCallId(block.id), block.name)
}
messages.push(assistant)
continue continue
} }
// user role: text + tool results (each result becomes its own message). // user role: text + tool results (each result becomes its own message).

View file

@ -60,7 +60,8 @@ import { launchEnvironmentOf } from '@deepseek-ai/dsh-launch-environment'
import { assertUsableApiKey, LlmError, resolveImageAttachmentAccess } from '@deepseek-ai/dsh-llm' import { assertUsableApiKey, LlmError, resolveImageAttachmentAccess } from '@deepseek-ai/dsh-llm'
import type { AdapterRegistrationHandle, DirectoryRegistrationHandle, LlmConfigurableProvider } from '@deepseek-ai/dsh-llm' import type { AdapterRegistrationHandle, DirectoryRegistrationHandle, LlmConfigurableProvider } from '@deepseek-ai/dsh-llm'
import type {} from '@deepseek-ai/dsh-fs' import type {} from '@deepseek-ai/dsh-fs'
import { deepEqualJson, installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings' import type {} from '@deepseek-ai/dsh-settings'
import { deepEqualJson } from '@deepseek-ai/dsh-util-values'
import { PiAiAdapter } from './adapter.ts' import { PiAiAdapter } from './adapter.ts'
import { authContextFrom, credentialStoreFrom } from './auth.ts' import { authContextFrom, credentialStoreFrom } from './auth.ts'
import { catalogProviderIds } from './catalog.ts' import { catalogProviderIds } from './catalog.ts'
@ -88,7 +89,7 @@ export { supportedProtocols } from './provider.ts'
export const name = 'llm-pi-ai' export const name = 'llm-pi-ai'
export const inject = ['llm'] export const inject = ['llm']
const NS = settingsNamespace('llm-pi-ai') const NS = 'llm-pi-ai'
/** /**
* The registry captures these per route; a change here must re-register. * The registry captures these per route; a change here must re-register.
@ -292,38 +293,40 @@ export function apply(ctx: Context, config: Config): void {
} }
ensureRegistrationFacts() ensureRegistrationFacts()
installSettingsSection(ctx, NS, Config, config, { ctx.inject(['settings'], (settingsCtx) => {
// Refuse an unserviceable section where it is written: without this a settingsCtx.settings.installSection(ctx, NS, Config, config, {
// schema-valid profile the adapter cannot serve would be stored and then // Refuse an unserviceable section where it is written: without this a
// silently disable every route in this namespace. // schema-valid profile the adapter cannot serve would be stored and then
validate: assertServiceable, // silently disable every route in this namespace.
setSource: (source) => { validate: assertServiceable,
current = source setSource: (source) => {
}, current = source
onChange: () => { },
// Named here rather than left to the settings watcher: `assertServiceable` onChange: () => {
// cannot see the llm registry, so a profile claiming a route another // Named here rather than left to the settings watcher: `assertServiceable`
// adapter family owns is stored successfully and only fails at this swap. // cannot see the llm registry, so a profile claiming a route another
// Without its own diagnostic that refusal reaches the operator as a // adapter family owns is stored successfully and only fails at this swap.
// generic "settings: watcher failed", naming neither the route nor why it // Without its own diagnostic that refusal reaches the operator as a
// is not serving. The previous routes keep serving either way. // generic "settings: watcher failed", naming neither the route nor why it
try { // is not serving. The previous routes keep serving either way.
ensureRegistrationFacts() try {
} catch (error) { ensureRegistrationFacts()
ctx.logger.error('llm-pi-ai: keeping the previously registered routes after a refused update') } catch (error) {
ctx.logger.error(error) ctx.logger.error('llm-pi-ai: keeping the previously registered routes after a refused update')
} ctx.logger.error(error)
// The directory follows the profiles the registry accepted, so a route }
// that failed to register is not advertised as configurable. A refused // The directory follows the profiles the registry accepted, so a route
// directory swap is contained here for the same reason the registry's // that failed to register is not advertised as configurable. A refused
// is: the previous entries keep serving, and `directoryFacts` stays put // directory swap is contained here for the same reason the registry's
// so returning to a working configuration re-applies. // is: the previous entries keep serving, and `directoryFacts` stays put
try { // so returning to a working configuration re-applies.
ensureDirectory() try {
} catch (error) { ensureDirectory()
ctx.logger.error('llm-pi-ai: keeping the previous configurable-provider directory after a refused update') } catch (error) {
ctx.logger.error(error) ctx.logger.error('llm-pi-ai: keeping the previous configurable-provider directory after a refused update')
} ctx.logger.error(error)
}, }
},
})
}) })
} }

View file

@ -8,8 +8,9 @@
* @module dsh-llm-pi-ai/stream * @module dsh-llm-pi-ai/stream
*/ */
import { ToolCallId, CONTEXT_WINDOW_EXCEEDED_CODE, EMPTY_RESPONSE_CODE, isContextWindowExceededError, isQuotaExceededError, LlmError, QUOTA_EXCEEDED_CODE } from '@deepseek-ai/dsh-llm' import { brandString } from '@deepseek-ai/dsh-brand'
import type { FinishReason, StreamChunk, TokenUsage } from '@deepseek-ai/dsh-llm' import { CONTEXT_WINDOW_EXCEEDED_CODE, EMPTY_RESPONSE_CODE, isContextWindowExceededError, isQuotaExceededError, LlmError, QUOTA_EXCEEDED_CODE } from '@deepseek-ai/dsh-llm'
import type { FinishReason, StreamChunk, TokenUsage, ToolCallId } from '@deepseek-ai/dsh-llm'
import { isContextOverflow } from '@earendil-works/pi-ai' import { isContextOverflow } from '@earendil-works/pi-ai'
import type { AssistantMessage, AssistantMessageEvent, Usage as PiUsage } from '@earendil-works/pi-ai' import type { AssistantMessage, AssistantMessageEvent, Usage as PiUsage } from '@earendil-works/pi-ai'
import { toPiReplayState } from './replay.ts' import { toPiReplayState } from './replay.ts'
@ -182,7 +183,7 @@ export async function* toStreamChunks(
yield { yield {
type: 'tool-call-delta', type: 'tool-call-delta',
index: event.contentIndex, index: event.contentIndex,
id: ToolCallId(known?.id ?? ''), id: brandString<ToolCallId>(known?.id ?? ''),
...known?.name !== undefined && known.name.length > 0 ? { name: known.name } : {}, ...known?.name !== undefined && known.name.length > 0 ? { name: known.name } : {},
argumentsDelta: event.delta, argumentsDelta: event.delta,
} }
@ -194,7 +195,7 @@ export async function* toStreamChunks(
index: event.contentIndex, index: event.contentIndex,
block: { block: {
type: 'tool-call', type: 'tool-call',
id: ToolCallId(event.toolCall.id), id: brandString<ToolCallId>(event.toolCall.id),
name: event.toolCall.name, name: event.toolCall.name,
// pi-ai hands back the PARSED arguments; the harness vocabulary // pi-ai hands back the PARSED arguments; the harness vocabulary
// keeps the raw string. // keeps the raw string.

View file

@ -6,7 +6,6 @@ import { Context } from '@deepseek-ai/cordis'
import LlmRuntime, { createUserMessage, ReasoningEffortId } from '@deepseek-ai/dsh-llm' import LlmRuntime, { createUserMessage, ReasoningEffortId } from '@deepseek-ai/dsh-llm'
import type { StreamChunk } from '@deepseek-ai/dsh-llm' import type { StreamChunk } from '@deepseek-ai/dsh-llm'
import FileSettingsProvider from '@deepseek-ai/dsh-settings-file' import FileSettingsProvider from '@deepseek-ai/dsh-settings-file'
import { settingsNamespace } from '@deepseek-ai/dsh-settings'
import * as LlmPiAi from '@deepseek-ai/dsh-llm-pi-ai' import * as LlmPiAi from '@deepseek-ai/dsh-llm-pi-ai'
import { PiAiAdapter } from '@deepseek-ai/dsh-llm-pi-ai' import { PiAiAdapter } from '@deepseek-ai/dsh-llm-pi-ai'
import { getBuiltinModels } from '@earendil-works/pi-ai/providers/all' import { getBuiltinModels } from '@earendil-works/pi-ai/providers/all'
@ -227,7 +226,7 @@ describe('hand-declared providers', () => {
// a written section, the plugin's own registration, and `ctx.llm`. // a written section, the plugin's own registration, and `ctx.llm`.
const dir = await home() const dir = await home()
const ctx = await bootWithSettings(dir, {}) const ctx = await bootWithSettings(dir, {})
await ctx.settings.update(settingsNamespace('llm-pi-ai'), { await ctx.settings.update('llm-pi-ai', {
providers: { providers: {
'acme-gateway': { 'acme-gateway': {
api: 'openai-completions', api: 'openai-completions',
@ -962,7 +961,7 @@ describe('compat switches', () => {
// and `Model.compat`. // and `Model.compat`.
const dir = await home() const dir = await home()
const ctx = await bootWithSettings(dir, {}) const ctx = await bootWithSettings(dir, {})
await expect(ctx.settings.update(settingsNamespace('llm-pi-ai'), { await expect(ctx.settings.update('llm-pi-ai', {
providers: { providers: {
'acme-gateway': { 'acme-gateway': {
api: 'openai-completions', api: 'openai-completions',
@ -981,7 +980,7 @@ describe('compat switches', () => {
const server = await mockServer([{ events: textEvents }]) const server = await mockServer([{ events: textEvents }])
const dir = await home() const dir = await home()
const ctx = await bootWithSettings(dir, {}) const ctx = await bootWithSettings(dir, {})
await ctx.settings.update(settingsNamespace('llm-pi-ai'), { await ctx.settings.update('llm-pi-ai', {
providers: { providers: {
'acme-gateway': { 'acme-gateway': {
apiKeyEnv: KEY_ENV, apiKeyEnv: KEY_ENV,
@ -1152,7 +1151,7 @@ describe('configurable-provider directory', () => {
const before = ctx.llm.listConfigurableProviders().length const before = ctx.llm.listConfigurableProviders().length
expect(before).toBeGreaterThan(30) expect(before).toBeGreaterThan(30)
await ctx.settings.update(settingsNamespace('llm-pi-ai'), { await ctx.settings.update('llm-pi-ai', {
providers: { providers: {
'deepseek-official': { 'deepseek-official': {
api: 'openai-completions', api: 'openai-completions',
@ -1174,7 +1173,7 @@ describe('configurable-provider directory', () => {
const ctx = await bootWithSettings(dir, {}) const ctx = await bootWithSettings(dir, {})
const catalogOnly = ctx.llm.listConfigurableProviders().length const catalogOnly = ctx.llm.listConfigurableProviders().length
await ctx.settings.update(settingsNamespace('llm-pi-ai'), { await ctx.settings.update('llm-pi-ai', {
providers: { providers: {
'acme-gateway': { 'acme-gateway': {
displayName: 'Acme Gateway', displayName: 'Acme Gateway',
@ -1188,7 +1187,7 @@ describe('configurable-provider directory', () => {
expect(ctx.llm.listConfigurableProviders().find(entry => entry.provider === 'acme-gateway')?.displayName) expect(ctx.llm.listConfigurableProviders().find(entry => entry.provider === 'acme-gateway')?.displayName)
.toBe('Acme Gateway') .toBe('Acme Gateway')
await ctx.settings.replace(settingsNamespace('llm-pi-ai'), {}) await ctx.settings.replace('llm-pi-ai', {})
expect(ctx.llm.listConfigurableProviders()).toHaveLength(catalogOnly) expect(ctx.llm.listConfigurableProviders()).toHaveLength(catalogOnly)
}) })

View file

@ -6,14 +6,13 @@ import { join } from 'node:path'
import LlmRuntime, { LlmAdapter } from '@deepseek-ai/dsh-llm' import LlmRuntime, { LlmAdapter } from '@deepseek-ai/dsh-llm'
import { credentialRef } from '@deepseek-ai/dsh-credentials' import { credentialRef } from '@deepseek-ai/dsh-credentials'
import { LocalCredentialProvider } from '@deepseek-ai/dsh-credentials-local' import { LocalCredentialProvider } from '@deepseek-ai/dsh-credentials-local'
import { settingsNamespace } from '@deepseek-ai/dsh-settings'
import { FileSettingsProvider } from '@deepseek-ai/dsh-settings-file' import { FileSettingsProvider } from '@deepseek-ai/dsh-settings-file'
import * as LlmPiAi from '@deepseek-ai/dsh-llm-pi-ai' import * as LlmPiAi from '@deepseek-ai/dsh-llm-pi-ai'
import AuthorizationService from '@deepseek-ai/dsh-authorization' import AuthorizationService from '@deepseek-ai/dsh-authorization'
import { assemble } from './assemble.ts' import { assemble } from './assemble.ts'
import { closeMockServers, mockServer, textEvents } from './mock-server.ts' import { closeMockServers, mockServer, textEvents } from './mock-server.ts'
const NS = settingsNamespace('llm-pi-ai') const NS = 'llm-pi-ai'
/** Minimal foreign adapter: only needs to own a route the pi-ai plugin then wants. */ /** Minimal foreign adapter: only needs to own a route the pi-ai plugin then wants. */
class StubAdapter extends LlmAdapter { class StubAdapter extends LlmAdapter {

View file

@ -11,6 +11,9 @@
{ {
"path": "../../util/launch-environment" "path": "../../util/launch-environment"
}, },
{
"path": "../../util/brand"
},
{ {
"path": "../../../vendor/cosmokit" "path": "../../../vendor/cosmokit"
}, },

View file

@ -199,7 +199,7 @@ describe('BlockAssembler replay metadata', () => {
describe('assertNever', () => { describe('assertNever', () => {
it('throws with diagnostics when a value escapes a closed union at runtime', async () => { it('throws with diagnostics when a value escapes a closed union at runtime', async () => {
const { assertNever } = await import('@deepseek-ai/dsh-llm') const { assertNever } = await import('@deepseek-ai/dsh-util-values')
expect(() => assertNever({ type: 'rogue' } as never, 'test-context')) expect(() => assertNever({ type: 'rogue' } as never, 'test-context'))
.toThrow('unreachable variant in test-context: {"type":"rogue"}') .toThrow('unreachable variant in test-context: {"type":"rogue"}')
expect(() => assertNever(undefined as never)).toThrow('unreachable variant: undefined') expect(() => assertNever(undefined as never)).toThrow('unreachable variant: undefined')

View file

@ -5,7 +5,8 @@
*/ */
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import { callConfigEquals, deepFreeze, isAgentLoopRequest, markAgentLoopRequest } from '../src/call-config.ts' import { deepFreeze } from '@deepseek-ai/dsh-util-values'
import { callConfigEquals, isAgentLoopRequest, markAgentLoopRequest } from '../src/call-config.ts'
import { ReasoningEffortId } from '../src/brand.ts' import { ReasoningEffortId } from '../src/brand.ts'
import type { GenerateOptions } from '../src/types.ts' import type { GenerateOptions } from '../src/types.ts'

View file

@ -11,10 +11,11 @@ import { dirname, isAbsolute, join, parse } from 'node:path'
import { fileURLToPath, pathToFileURL } from 'node:url' import { fileURLToPath, pathToFileURL } from 'node:url'
import { FiberState, type Context } from '@deepseek-ai/cordis' import { FiberState, type Context } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery' import z from '@deepseek-ai/schemastery'
import { brandString } from '@deepseek-ai/dsh-brand'
import type { Entry, EntryTree } from '@deepseek-ai/cordis-plugin-loader' import type { Entry, EntryTree } from '@deepseek-ai/cordis-plugin-loader'
import type {} from '@deepseek-ai/dsh-agent' import type {} from '@deepseek-ai/dsh-agent'
import type {} from '@deepseek-ai/dsh-deepseek-llm-api-extensions' import type {} from '@deepseek-ai/dsh-deepseek-llm-api-extensions'
import { SessionId } from '@deepseek-ai/dsh-session' import type { SessionId } from '@deepseek-ai/dsh-session'
import type {} from '@deepseek-ai/dsh-agent-presets' import type {} from '@deepseek-ai/dsh-agent-presets'
import type { DeepSeekPluginPackageIdentity, DeepSeekPluginPackageInventoryExtension } from './types.ts' import type { DeepSeekPluginPackageIdentity, DeepSeekPluginPackageInventoryExtension } from './types.ts'
import type {} from './types.ts' import type {} from './types.ts'
@ -155,7 +156,7 @@ async function collectActivePluginPackages(
): Promise<DeepSeekPluginPackageIdentity[]> { ): Promise<DeepSeekPluginPackageIdentity[]> {
const entries = activeEntries(ctx.loader) const entries = activeEntries(ctx.loader)
if (sessionId !== undefined && ctx.get('agentPresets') !== undefined) { if (sessionId !== undefined && ctx.get('agentPresets') !== undefined) {
const agent = ctx.agents.get(SessionId(sessionId)) const agent = ctx.agents.get(brandString<SessionId>(sessionId))
if (agent !== undefined) { if (agent !== undefined) {
// The optional peer is loaded only when its service is present. Its existing // The optional peer is loaded only when its service is present. Its existing
// mount query keeps Loader internals off the public AgentPresets service. // mount query keeps Loader internals off the public AgentPresets service.

View file

@ -6,8 +6,9 @@
import { Context, Service } from '@deepseek-ai/cordis' import { Context, Service } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery' import z from '@deepseek-ai/schemastery'
import { BlockAssembler, deepFreeze } from '@deepseek-ai/dsh-llm' import { BlockAssembler } from '@deepseek-ai/dsh-llm'
import type { LlmImageRequestPricing, Message, TokenUsage } from '@deepseek-ai/dsh-llm' import type { LlmImageRequestPricing, Message, TokenUsage } from '@deepseek-ai/dsh-llm'
import { deepFreeze } from '@deepseek-ai/dsh-util-values'
import type { EpochHeader, Session, SessionEvent } from '@deepseek-ai/dsh-session' import type { EpochHeader, Session, SessionEvent } from '@deepseek-ai/dsh-session'
import { canonicalHeader, headerEquals, isSurfaceEvent } from '@deepseek-ai/dsh-session' import { canonicalHeader, headerEquals, isSurfaceEvent } from '@deepseek-ai/dsh-session'
// Type-only: activates the `ctx.sessionProjections` Context declaration. // Type-only: activates the `ctx.sessionProjections` Context declaration.

View file

@ -12,7 +12,7 @@ import type {
LspRange, LspRange,
} from '@deepseek-ai/dsh-lsp' } from '@deepseek-ai/dsh-lsp'
import { LspError } from '@deepseek-ai/dsh-lsp' import { LspError } from '@deepseek-ai/dsh-lsp'
import { assertNever } from '@deepseek-ai/dsh-llm' import { assertNever } from '@deepseek-ai/dsh-util-values'
import type { import type {
WireHover, WireHover,
WireLocation, WireLocation,

View file

@ -13,11 +13,10 @@
import type { Context } from '@deepseek-ai/cordis' import type { Context } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery' import z from '@deepseek-ai/schemastery'
import { defineTool } from '@deepseek-ai/dsh-tools' import { defineTool } from '@deepseek-ai/dsh-tools'
import { assertNever } from '@deepseek-ai/dsh-llm'
import { LspError } from '@deepseek-ai/dsh-lsp' import { LspError } from '@deepseek-ai/dsh-lsp'
import type {} from '@deepseek-ai/dsh-lsp' import type {} from '@deepseek-ai/dsh-lsp'
import { FIRST_PARTY_SECTION_ORDER } from '@deepseek-ai/dsh-system-prompt'
import { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout' import { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout'
import { assertNever } from '@deepseek-ai/dsh-util-values'
import { import {
DEFAULT_MAX_LOCATIONS, DEFAULT_MAX_LOCATIONS,
DEFAULT_MAX_RESULT_CHARS, DEFAULT_MAX_RESULT_CHARS,
@ -103,7 +102,7 @@ export function apply(ctx: Context, config: Config): void {
ctx.systemPrompt.section({ ctx.systemPrompt.section({
name: 'tool:lsp', name: 'tool:lsp',
order: FIRST_PARTY_SECTION_ORDER.TOOL_LSP, order: ctx.systemPrompt.getSectionOrder('TOOL_LSP'),
text: LSP_PROMPT_TEXT, text: LSP_PROMPT_TEXT,
}) })

View file

@ -23,7 +23,8 @@ import type { AttachmentStore, ImageAttachmentRef, ImageMediaType, SaveImageAtta
import type { ContentBlock } from '@deepseek-ai/dsh-llm' import type { ContentBlock } from '@deepseek-ai/dsh-llm'
import type { ToolDefinition, ToolExecution, ToolExecutionResult } from '@deepseek-ai/dsh-tools' import type { ToolDefinition, ToolExecution, ToolExecutionResult } from '@deepseek-ai/dsh-tools'
import { assertSupportedJsonSchema } from '@deepseek-ai/dsh-tools' import { assertSupportedJsonSchema } from '@deepseek-ai/dsh-tools'
import type { JsonSchemaNode, JsonValue } from '@deepseek-ai/dsh-tools' import type { JsonSchemaNode } from '@deepseek-ai/dsh-tools'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
/** Resolved options relevant to tool bridging. */ /** Resolved options relevant to tool bridging. */
export interface ToolBridgeOptions { export interface ToolBridgeOptions {

View file

@ -8,7 +8,8 @@ import { ToolCallId, LlmAdapter, LlmRuntime } from '@deepseek-ai/dsh-llm'
import type { ContentBlock } from '@deepseek-ai/dsh-llm' import type { ContentBlock } from '@deepseek-ai/dsh-llm'
import type { GenerateOptions, LlmResolvedModelInfo, StreamChunk } from '@deepseek-ai/dsh-llm' import type { GenerateOptions, LlmResolvedModelInfo, StreamChunk } from '@deepseek-ai/dsh-llm'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt' import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRuntime, { type JsonValue } from '@deepseek-ai/dsh-tools' import ToolRuntime from '@deepseek-ai/dsh-tools'
import type { JsonValue } from '@deepseek-ai/dsh-util-values'
import type { PostToolDecision } from '@deepseek-ai/dsh-tools' import type { PostToolDecision } from '@deepseek-ai/dsh-tools'
import { publicToolName, syncTools, type ToolBridgeOptions } from '@deepseek-ai/dsh-mcp-client/src/tools.ts' import { publicToolName, syncTools, type ToolBridgeOptions } from '@deepseek-ai/dsh-mcp-client/src/tools.ts'
import { createTransport } from '@deepseek-ai/dsh-mcp-client/src/transport.ts' import { createTransport } from '@deepseek-ai/dsh-mcp-client/src/transport.ts'

Some files were not shown because too many files have changed in this diff Show more