Merge remote-tracking branch 'origin/master' into feat/web-message-feedback-ui
Resolve additive conflicts in the api-remotes client assembly by keeping both the message-feedback remote mount and master's forwarded-event allowlist, and regenerate the module graph.
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@
|
||||
* Watch-build for client-plugin HMR: runs every `dsh.client` plugin package
|
||||
* through the tsdown JS API in watch mode. Reload signaling is not this
|
||||
* script's business — the host webserver stat-polls the bundles it serves and
|
||||
* broadcasts `rebuilt` frames itself (`dsh web --dev`), so any process that
|
||||
* broadcasts `rebuilt` frames itself (`dsh web`), so any process that
|
||||
* rewrites `lib/client.js` files triggers reloads; this script is merely the
|
||||
* convenient way to keep them all rebuilt on source change.
|
||||
*
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"docs/testing.md": 1150,
|
||||
"examples/AGENTS.md": 310,
|
||||
"packages/AGENTS.md": 675,
|
||||
"packages/README.md": 980
|
||||
"packages/README.md": 994
|
||||
}
|
||||
@@ -117,12 +117,12 @@ export const SERVICE_WALK_EXEMPTIONS: Record<string, string> = {
|
||||
configuredAgentIdentities: 'not a service: launcher-provided boot-context value (ConfiguredAgentIdentities | undefined) — packages/core/agent-loop/README.md owns this launcher contract',
|
||||
launcherSessionQueryPath: 'not a service: launcher-provided boot-context value (string | undefined) — packages/session-query/session-query-sqlite/README.md owns this launcher contract',
|
||||
dshHomePath: 'not a service: boot-provided root accessor function (typeof dshHomePath | undefined) for Loader !!js config expressions — packages/boot/app-boot/README.md owns the boot contract',
|
||||
headlessIo: 'not a service: launcher-provided root accessor value (HeadlessIo | undefined) for the headless bundle runner — packages/bundle/headless/README.md owns this launcher contract',
|
||||
launcherEnvironment: 'not a service: launcher-provided root accessor value (EnvironmentSnapshot | undefined) — packages/util/environment/README.md owns this launcher contract',
|
||||
lsp: 'interface-typed (LspService); implementing class Lsp is not the declared type name — packages/lsp/lsp/README.md owns the API',
|
||||
apiProxy: 'interface-typed (ApiProxy) with the class in api-proxy.ts, not index.ts — packages/host/apiproxy/README.md owns the API',
|
||||
appShell: 'client-side interface-typed browser service — packages/client/web/README.md owns the API',
|
||||
connection: 'client-side interface-typed browser service — packages/client/connection/README.md owns the API',
|
||||
settingsScope: 'client-side settings-namespace transport service — packages/client/ui-settings/README.md owns the API',
|
||||
chatFileMentions: 'client-side slot-contract accessor (ChatFileMentions) — packages/client/ui-conversation/README.md owns the API',
|
||||
command: 'client-side interface-typed browser service — packages/client/ui-command/README.md owns the API',
|
||||
conversation: 'client-side interface-typed browser service — packages/client/ui-conversation/README.md owns the API',
|
||||
@@ -149,6 +149,7 @@ export const SERVICE_WALK_EXEMPTIONS: Record<string, string> = {
|
||||
export const EVENT_SCOPE_PAGE: Record<string, string> = {
|
||||
'agent': 'core.md',
|
||||
'agent-loop': 'core.md',
|
||||
'agent-preset': 'core.md',
|
||||
'approval': 'approval.md',
|
||||
'commands': 'commands.md',
|
||||
'credentials': 'credentials.md',
|
||||
@@ -173,18 +174,12 @@ export const EVENT_SCOPE_PAGE: Record<string, string> = {
|
||||
* scan reads EVERY `declare module '@deepseek-ai/cordis'` Events merge under
|
||||
* `packages/x/x/src/**`, so a declared event either renders onto a subsystems
|
||||
* page (via {@link EVENT_SCOPE_PAGE}) or names itself here — never vanishes
|
||||
* silently. Keys are full event names, not scopes: client-face events share
|
||||
* scopes with rendered host events (`commands/changed` beside `commands/*`),
|
||||
* so a scope-level exemption would mask a host-face regression.
|
||||
* silently. Keys are full event names rather than scopes, so a scope-level
|
||||
* exemption cannot mask another declaration in that scope.
|
||||
*/
|
||||
export const EVENT_WALK_EXEMPTIONS: Record<string, string> = {
|
||||
'commands/changed': 'client-face registry invalidation signal — packages/client/runtime/README.md owns the API',
|
||||
'connection/reset': 'client-face transport signal — packages/client/runtime/README.md owns the API',
|
||||
'credentials/changed': 'client-face registry invalidation signal — packages/client/runtime/README.md owns the API',
|
||||
'locale/change': 'client-face locale switch signal — packages/client/locale/README.md owns the API',
|
||||
'models/changed': 'client-face registry invalidation signal — packages/client/runtime/README.md owns the API',
|
||||
'session/preset-changed': 'client-face per-session catalog invalidation signal — packages/client/runtime/README.md owns the API',
|
||||
'settings/changed': 'client-face registry invalidation signal — packages/client/runtime/README.md owns the API',
|
||||
'slash/input-begin-command': 'client-face slash-input protocol — packages/client/ui-slash/README.md owns the API',
|
||||
'slash/input-consume-token': 'client-face slash-input protocol — packages/client/ui-slash/README.md owns the API',
|
||||
'slash/input-insert-reference': 'client-face slash-input protocol — packages/client/ui-slash/README.md owns the API',
|
||||
|
||||
@@ -758,10 +758,8 @@ const EVENT_API_METHODS = new Set(['on', 'once', 'emit', 'parallel', 'serial', '
|
||||
* documents why: one program cannot hold both faces' Context merges), so a
|
||||
* Client package enters only when a host file imports it. Client-face
|
||||
* listeners on client-face events are therefore under-reported —
|
||||
* `connection/reset` omits `ui-skill`/`ui-agent-preset`, `models/changed`
|
||||
* omits `ui-model`, `session/preset-changed` omits `ui-skill`. Closing it
|
||||
* needs a second Client program whose relations merge into these, not a
|
||||
* wider seed.
|
||||
* `connection/reset` omits `ui-skill`/`ui-agent-preset`. Closing it needs a
|
||||
* second Client program whose relations merge into these, not a wider seed.
|
||||
*/
|
||||
export class EventRelationCollector {
|
||||
private readonly relations = new Map<string, EventRelation>()
|
||||
|
||||
@@ -40,6 +40,7 @@ const LINK_MAP: Record<string, string> = {
|
||||
CallId: 'core.md',
|
||||
ContentBlock: 'core.md',
|
||||
MessageSource: 'core.md',
|
||||
ScheduleChange: 'schedule.md',
|
||||
StreamChunk: 'llm-streaming.md',
|
||||
TokenUsage: 'llm-streaming.md',
|
||||
TodoItem: 'session.md',
|
||||
|
||||
@@ -50,6 +50,7 @@ import * as ToolStrReplaceEditor from '@deepseek-ai/dsh-tool-str-replace-editor'
|
||||
import PtyService from '@deepseek-ai/dsh-pty'
|
||||
import * as ToolPty from '@deepseek-ai/dsh-tool-pty'
|
||||
import * as ToolGoal from '@deepseek-ai/dsh-tool-goal'
|
||||
import * as ToolSchedule from '@deepseek-ai/dsh-tool-schedule'
|
||||
import Lsp from '@deepseek-ai/dsh-lsp'
|
||||
import * as ToolLsp from '@deepseek-ai/dsh-tool-lsp'
|
||||
import * as ToolSkill from '@deepseek-ai/dsh-tool-skill'
|
||||
@@ -114,15 +115,18 @@ const catalogChildScopes = new WeakMap<Context, Agent>()
|
||||
* schema harvest, without starting a model, Agent loop, or persistence backend.
|
||||
* @param ctx - catalog context owning the scope.
|
||||
* @param mountScoped - package installer for the scoped context.
|
||||
* @param key - agent-like scope key exposed to the package's scope selector.
|
||||
* @param inject - services the package installer must await before mounting.
|
||||
*/
|
||||
async function mountCatalogChildScope(
|
||||
ctx: Context,
|
||||
mountScoped: (childCtx: Context) => void,
|
||||
key: Agent = { id: SessionId('tool-catalog-child') } as Agent,
|
||||
inject: string[] = ['tools', 'systemPrompt', 'subagents'],
|
||||
): Promise<void> {
|
||||
const key = { id: SessionId('tool-catalog-child') } as Agent
|
||||
await ctx.plugin(Object.assign((inner: Context) => {
|
||||
mountScoped(createScope(inner, key).ctx)
|
||||
}, { inject: ['tools', 'systemPrompt', 'subagents'] }))
|
||||
}, { inject }))
|
||||
catalogChildScopes.set(ctx, key)
|
||||
}
|
||||
|
||||
@@ -348,6 +352,27 @@ const TOOL_PACKAGES: ToolPackage[] = [
|
||||
note:
|
||||
'create, edit, pause, and resume require direct-human root authority; complete and blocked also accept the exact current goal round. The default blocked lower bound is three admitted rounds.',
|
||||
},
|
||||
{
|
||||
pkg: '@deepseek-ai/dsh-tool-schedule',
|
||||
dir: 'tool-schedule',
|
||||
source: 'packages/schedule/tool-schedule/src/tools.ts',
|
||||
requires: ['ctx.tools', 'ctx.sessions', 'Session persistence', 'a future live root Agent'],
|
||||
writes: ['tool/call', 'schedule/change create or delete', 'tool/result'],
|
||||
async mount(ctx) {
|
||||
await ctx.plugin(SessionStore)
|
||||
const session = ctx.sessions.create(SessionId('tool-catalog-schedule'))
|
||||
const agent = { id: session.id, session } as Agent
|
||||
await mountCatalogChildScope(ctx, (childCtx) => {
|
||||
ToolSchedule.registerScheduleTools(ctx, childCtx, agent, () => {})
|
||||
}, agent, ['tools', 'systemPrompt'])
|
||||
},
|
||||
scope: ctx => catalogChildScopes.get(ctx) as Agent,
|
||||
note:
|
||||
'Registered only inside live root Agent scopes created after the opt-in Schedule plugin loads. '
|
||||
+ 'Version 1 accepts after_seconds, explicit absolute at, and bounded fixed-rate every_seconds, '
|
||||
+ 'and discloses session-local delivery; '
|
||||
+ 'management reads and mutations require the shared Session persistence barrier.',
|
||||
},
|
||||
{
|
||||
pkg: '@deepseek-ai/dsh-tool-lsp',
|
||||
dir: 'tool-lsp',
|
||||
|
||||
@@ -251,6 +251,19 @@ describe('rewriteMarkdown', () => {
|
||||
})
|
||||
|
||||
describe('docsPages locale routes', () => {
|
||||
it('redirects both locale roots to their locale-relative quick-start page', () => {
|
||||
const homes = docsPages.filter(page => page.sidebar === null)
|
||||
expect(homes.map(page => page.route).sort()).toEqual(['en/index.md', 'index.md'])
|
||||
for (const page of homes) {
|
||||
const source = readFileSync(resolve(repositoryRoot, page.source), 'utf8')
|
||||
const projected = projectedPageContent(source, page)
|
||||
expect(projected).toContain('layout: false')
|
||||
expect(projected).toContain('http-equiv: refresh')
|
||||
expect(projected).toContain('content: 0; url=./guide/quickstart')
|
||||
expect(projected).not.toContain('# DeepSeek Harness')
|
||||
}
|
||||
})
|
||||
|
||||
it('publishes every route in both locales and uses every available Chinese counterpart', () => {
|
||||
const byRoute = new Map(docsPages.map(page => [page.route, page]))
|
||||
for (const page of docsPages.filter(page => page.locale === 'root')) {
|
||||
@@ -294,7 +307,7 @@ describe('docsPages locale routes', () => {
|
||||
const translated = rootPages.filter(page => page.contentLocale === 'zh-CN')
|
||||
const fallbacks = rootPages.filter(page => page.contentLocale === 'en-US')
|
||||
|
||||
expect(translated).toHaveLength(42)
|
||||
expect(translated).toHaveLength(43)
|
||||
expect(translated.every(page => page.source.endsWith('.zh.md'))).toBe(true)
|
||||
expect(fallbacks).toEqual([])
|
||||
})
|
||||
@@ -388,9 +401,9 @@ describe('projectedPageContent', () => {
|
||||
|
||||
it('omits the source-only body from locale home pages', () => {
|
||||
expect(projectedPageContent(
|
||||
'---\nlayout: home\nhero:\n name: Harness\n---\n\n# Harness\n\n[English](index.md) | 中文\n',
|
||||
'---\nlayout: false\nhead:\n - - meta\n - http-equiv: refresh\n content: 0; url=./guide/quickstart\n---\n\n# Harness\n\n[English](index.md) | 中文\n',
|
||||
page(null),
|
||||
)).toBe('---\nlayout: home\nhero:\n name: Harness\n---\n')
|
||||
)).toBe('---\nlayout: false\nhead:\n - - meta\n - http-equiv: refresh\n content: 0; url=./guide/quickstart\n---\n')
|
||||
})
|
||||
|
||||
it('keeps the full body for ordinary pages', () => {
|
||||
|
||||
@@ -242,8 +242,8 @@ const EXACT_EDITS: readonly ExactEdit[] = [
|
||||
{
|
||||
id: 'vendor-readme-local-modification-log',
|
||||
file: 'vendor/README.md',
|
||||
find: '\n18. **`cordis/package.json` publishes `src`**',
|
||||
replace: '\n17. **`@deepseek-ai` rescope**: every vendored manifest `name`, every internal dependency entry among the vendored set, and every module specifier that reaches them use the scoped names in the manifest table\'s `npm name` column. Directory names, version numbers, and dependency ranges are unchanged, and no upstream runtime identifier is renamed — `Symbol.for(\'schemastery\')` and Schemastery\'s `vendor:` metadata field keep their upstream values. Re-apply with `pnpm run rescope-vendor --apply` after a sync; the table\'s two name columns are the mapping, restated for consumers in [docs/rescope.md](../docs/rescope.md).\n18. **`cordis/package.json` publishes `src`**',
|
||||
find: '\n16. **`cordis/package.json` publishes `src`**',
|
||||
replace: '\n16. **`cordis/package.json` publishes `src`**: added `src` to the `files` list, joining the other eight vendored packages. Cordis declares `"./src/*": "./src/*"` in its exports, so a tarball without `src` publishes an export map pointing at absent files; the release change judgement also reads `files` to decide whether a diff reaches the payload, and a package whose only published paths are build output has no tracked path to match.\n17. **`@deepseek-ai` rescope**: every vendored manifest `name`, every internal dependency entry among the vendored set, and every module specifier that reaches them use the scoped names in the manifest table\'s `npm name` column. Directory names, version numbers, and dependency ranges are unchanged, and no upstream runtime identifier is renamed — `Symbol.for(\'schemastery\')` and Schemastery\'s `vendor:` metadata field keep their upstream values. Re-apply with `pnpm run rescope-vendor --apply` after a sync; the table\'s two name columns are the mapping, restated for consumers in [docs/rescope.md](../docs/rescope.md).',
|
||||
expect: 1,
|
||||
},
|
||||
{
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -182,6 +182,9 @@ describe('translation pairing records', () => {
|
||||
describe('translation scope discovery', () => {
|
||||
it.each([
|
||||
'README.md',
|
||||
'CONTRIBUTING.md',
|
||||
'CONTRIBUTING.zh.md',
|
||||
'CONTRIBUTING.i18n.yaml',
|
||||
'apps/cli/README.md',
|
||||
'future/subtree/readme.md',
|
||||
'packages/example/README.zh.md',
|
||||
@@ -195,6 +198,7 @@ describe('translation scope discovery', () => {
|
||||
|
||||
it.each([
|
||||
'packages/example/guide.md',
|
||||
'packages/example/CONTRIBUTING.md',
|
||||
'examples/tutorial.md',
|
||||
'website/reference.md',
|
||||
'packages/example/README.txt',
|
||||
|
||||
@@ -125,6 +125,7 @@ export interface TranslationPairingManifest {
|
||||
}
|
||||
|
||||
const README_ARTIFACT = /(?:^|\/)readme(?:\.md|\.zh\.md|\.i18n\.yaml)$/i
|
||||
const ROOT_CONTRIBUTING_ARTIFACT = /^contributing(?:\.md|\.zh\.md|\.i18n\.yaml)$/i
|
||||
const NON_SOURCE_DIRECTORIES = new Set([
|
||||
'node_modules',
|
||||
'lib',
|
||||
@@ -179,6 +180,7 @@ function isTranslationSourceExcluded(file: string): boolean {
|
||||
export function isTranslationScopeFile(file: string): boolean {
|
||||
return !file.startsWith('.agents/notes/archived/')
|
||||
&& !isTranslationSourceExcluded(file) && (README_ARTIFACT.test(file)
|
||||
|| ROOT_CONTRIBUTING_ARTIFACT.test(file)
|
||||
|| file.startsWith('.agents/notes/')
|
||||
|| file.startsWith('docs/')
|
||||
|| file.startsWith('python/'))
|
||||
|
||||
@@ -221,6 +221,86 @@
|
||||
"symbol": "GoalChanged",
|
||||
"source": "packages/goal/goal/src/domain.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "AfterScheduleRecord",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "AtScheduleRecord",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "EveryScheduleRecord",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "LocalAtInput",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "AtInput",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "OneShotScheduleRecord",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "ScheduleRecord",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "ScheduleCreateChange",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "ScheduleDeleteChange",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "OneShotScheduleDispatchChange",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "EveryScheduleDispatchChange",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "ScheduleDispatchChange",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "ScheduleChange",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "ScheduleState",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "ScheduleDeliveryMode",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/schedule.md",
|
||||
"symbol": "ScheduleView",
|
||||
"source": "packages/schedule/tool-schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/commands.md",
|
||||
"symbol": "CommandInputDescriptor",
|
||||
@@ -1288,7 +1368,7 @@
|
||||
{
|
||||
"doc": "docs/subsystems/workflow.md",
|
||||
"symbol": "WorkflowStartRequest",
|
||||
"source": "packages/workflow/workflow/src/types.ts"
|
||||
"source": "packages/workflow/workflow/src/runtime-types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/workflow.md",
|
||||
@@ -1303,7 +1383,7 @@
|
||||
{
|
||||
"doc": "docs/subsystems/workflow.md",
|
||||
"symbol": "WorkflowRun",
|
||||
"source": "packages/workflow/workflow/src/types.ts"
|
||||
"source": "packages/workflow/workflow/src/runtime-types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/lsp.md",
|
||||
@@ -1483,7 +1563,7 @@
|
||||
{
|
||||
"doc": "docs/subsystems/settings.md",
|
||||
"symbol": "SettingsNamespace",
|
||||
"source": "packages/settings/settings/src/index.ts"
|
||||
"source": "packages/settings/settings/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/settings.md",
|
||||
@@ -1508,7 +1588,7 @@
|
||||
{
|
||||
"doc": "docs/subsystems/settings.md",
|
||||
"symbol": "SettingsUpdateSource",
|
||||
"source": "packages/settings/settings/src/index.ts"
|
||||
"source": "packages/settings/settings/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/credentials.md",
|
||||
@@ -1713,7 +1793,7 @@
|
||||
{
|
||||
"doc": "docs/subsystems/credentials.md",
|
||||
"symbol": "CredentialRef",
|
||||
"source": "packages/credentials/credentials/src/index.ts"
|
||||
"source": "packages/credentials/credentials/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/permission.md",
|
||||
|
||||
@@ -33,6 +33,7 @@ const root = resolve(import.meta.dirname, '..')
|
||||
// specifiers resolve from apps/cli rather than the examples workspace.
|
||||
const appOverlayFiles = new Set([
|
||||
'examples/web-cordis/cordis.yml',
|
||||
'examples/web-schedule/cordis.yml',
|
||||
...globSync('examples/mcp-memory/*.cordis.yml', { cwd: root }),
|
||||
])
|
||||
const metadataFields = ['id', 'name', 'group', 'disabled', 'inject', 'intercept', 'isolate'] as const
|
||||
@@ -79,6 +80,7 @@ errors.push(...validateExampleResolution())
|
||||
errors.push(...validateAppResolution())
|
||||
errors.push(...validateSourcePlaneResolution())
|
||||
errors.push(...validatePresetPlaneSeparation())
|
||||
errors.push(...validateClientHalvesDeclared())
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.error('verify-cordis-config: invalid Loader metadata or plugin package resolution:')
|
||||
@@ -88,6 +90,35 @@ if (errors.length > 0) {
|
||||
console.log(`verify-cordis-config: ${files.length} config files passed.`)
|
||||
}
|
||||
|
||||
/**
|
||||
* A browser plugin must declare the browser half it ships.
|
||||
*
|
||||
* The browser roster is discovered by scanning composed packages for a
|
||||
* `dsh.client` block, and the node half of a surface plugin is an empty
|
||||
* `apply`. A `packages/client` package that exports `./client` without that
|
||||
* block therefore composes, activates, and contributes nothing — its bundle is
|
||||
* never served and no error is raised anywhere. The mismatch is invisible in
|
||||
* the composition file, so it is checked against the manifests instead. Only
|
||||
* this group is checked: a Host package's `./client` export is the typed wire
|
||||
* face its browser consumers import, not a plugin the roster serves.
|
||||
* @returns one violation per client package whose `./client` export and
|
||||
* `dsh.client` declaration disagree.
|
||||
*/
|
||||
function validateClientHalvesDeclared(): string[] {
|
||||
return globSync('packages/client/*/package.json', { cwd: root }).flatMap((manifestPath) => {
|
||||
const manifest = readManifest(manifestPath) as PackageManifest & {
|
||||
exports?: Record<string, unknown>
|
||||
dsh?: { client?: unknown }
|
||||
}
|
||||
const shipsClient = manifest.exports !== undefined && Object.hasOwn(manifest.exports, './client')
|
||||
const declaresClient = manifest.dsh?.client !== undefined
|
||||
if (shipsClient === declaresClient) return []
|
||||
return [shipsClient
|
||||
? `${manifestPath}: exports "./client" but declares no dsh.client, so its browser half is never served`
|
||||
: `${manifestPath}: declares dsh.client but exports no "./client" entry to serve`]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* No shipped agent preset may repeat a row the host composition still runs.
|
||||
*
|
||||
|
||||
@@ -84,7 +84,9 @@ const mermaid = (await import('mermaid')).default
|
||||
// maxEdges: mermaid's default 500-edge render guard; the module graph grows
|
||||
// with every package edge and crossed it legitimately. Raise the guard here
|
||||
// (a secure config settable only via initialize) rather than trimming edges.
|
||||
mermaid.initialize({ startOnLoad: false, maxEdges: 1000 })
|
||||
// The graph passed 1000 the same way it passed 500, so the headroom doubles
|
||||
// again rather than being set to whatever the current count happens to be.
|
||||
mermaid.initialize({ startOnLoad: false, maxEdges: 2000 })
|
||||
for (const block of blocks) {
|
||||
try {
|
||||
await mermaid.parse(block.source, { suppressErrors: false })
|
||||
|
||||
@@ -61,6 +61,7 @@ const SENTENCE_MODEL_EXPERIENCE: Readonly<Record<string, SentenceContract>> = {
|
||||
'packages/client/modules': { kind: 'none', reason: 'Browser-side module-loading kernel machinery; registers nothing model-facing.' },
|
||||
'packages/client/test-runtime': { kind: 'none', reason: 'Browser-side test infrastructure (jsdom bench); registers nothing model-facing.' },
|
||||
'packages/client/ui-slots': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/ui-attachment': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/ui-primitives': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/web-react': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/schema-form': { kind: 'none', reason: 'Browser-side form-rendering library; registers nothing model-facing.' },
|
||||
@@ -74,11 +75,13 @@ const SENTENCE_MODEL_EXPERIENCE: Readonly<Record<string, SentenceContract>> = {
|
||||
'packages/client/ui-tool': { kind: 'none', reason: 'Browser-side Tool presentation layer; renders logged calls without changing model context.' },
|
||||
'packages/client/ui-deliverables': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/ui-task': { kind: 'none', reason: 'Browser-side read-only projection of ctx.tasks records; dsh-tool-tasks owns the model-facing behavior.' },
|
||||
'packages/client/ui-workflow-run': { kind: 'none', reason: 'Browser-side UI plugin layer; renders durable workflow records without changing model context.' },
|
||||
'packages/client/ui-slash': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
'packages/client/ui-command': { kind: 'indirect', reason: 'The dispatch paths trigger the host command.execute RPC; each command handler\'s host package owns any model-visible effect.' },
|
||||
'packages/client/ui-model': { kind: 'indirect', reason: 'Selection routes session.selectModel; the Host snapshots the selection at the next prompt-assembly boundary and owns the model-visible effect.' },
|
||||
'packages/client/ui-goal': { kind: 'indirect', reason: 'The strip verbs route goal.* mutations; the host GoalService owns the model-visible goal/change context message.' },
|
||||
'packages/client/ui-permission': { kind: 'indirect', reason: 'The picker submits the host /permission command; the knob events it appends own the model-visible effect through the sandbox/approval consumers.' },
|
||||
'packages/client/ui-plugin-config': { kind: 'none', reason: 'Browser-side settings surface; registers no model surface.' },
|
||||
'packages/client/ui-plan': { kind: 'indirect', reason: 'The chip dispatches /plan off; dsh-plan-mode owns the model-visible policy, exit tool, and logged state.' },
|
||||
'packages/client/ui-question': { kind: 'indirect', reason: 'The package mounts dsh-tool-ask-user; that tool owns the model-visible schema and answer rendering.' },
|
||||
'packages/client/ui-trajectory': { kind: 'none', reason: 'Browser-side UI plugin layer; registers nothing model-facing.' },
|
||||
@@ -125,7 +128,7 @@ const SENTENCE_MODEL_EXPERIENCE: Readonly<Record<string, SentenceContract>> = {
|
||||
'packages/util/atomic-write': { kind: 'none', reason: 'Pure filesystem write primitive; registers nothing model-facing.' },
|
||||
'packages/session/session-telemetry': { kind: 'none', reason: 'The seam observes the session stream and hands redacted copies outward; it registers nothing model-facing.' },
|
||||
'packages/session/session-telemetry-otel': { kind: 'none', reason: 'The backend forwards seam records into the OTel SDK pipeline and registers nothing model-facing.' },
|
||||
'packages/session/user-id': { kind: 'none', reason: 'The shared identifier appears only in telemetry metadata and a direct human command response; it registers nothing model-facing.' },
|
||||
'packages/session/user-id': { kind: 'none', reason: 'The shared identifier reaches DeepSeek only as model-hidden HTTP metadata; it registers nothing model-facing.' },
|
||||
'packages/skill/skill': { kind: 'indirect', reason: 'The provider registry delegates model rendering to dsh-tool-skill.' },
|
||||
'packages/skill/skill-badge': { kind: 'indirect', reason: 'The bundled provider delegates model rendering to dsh-tool-skill.' },
|
||||
'packages/skill/skill-local': { kind: 'indirect', reason: 'The provider backend delegates model rendering to dsh-tool-skill.' },
|
||||
|
||||
Reference in New Issue
Block a user