Commit Graph
202 Commits
Author SHA1 Message Date
imccyu 00d541438b release(dsh): 0.2.2-alpha.2 2026-10-28 02:03:15 +08:00
imccyu 69c389ae11 release(dsh): 0.2.2-alpha.1 2026-10-28 00:00:58 +08:00
imccyu 3014b0af0f Merge master into DSH 0.2.1-rc.4 synchronization branch 2026-10-26 01:29:12 +08:00
imccyu 341beb33c0 release(dsh): 0.2.1-rc.4 2026-10-26 00:57:37 +08:00
imccyu 14f0b2aafe Merge master into DSH 0.2.1-rc.3 synchronization branch 2026-10-23 11:56:34 +08:00
imccyu d02ea370ee release(dsh): 0.2.1-rc.3 2026-10-23 01:11:35 +08:00
07akioni 84edeadc2b feat(i18n): add Spanish French German Russian and Portuguese 2026-10-22 19:53:52 +08:00
Yichen Jiang fb378dacf2 fix(web): offer DeepSeek web tools whenever a credential exists
The DeepSeek search and Search/Fetch API providers now report readiness
from credential presence alone, on any model route: the account token
first on the deepseek-account route, then a literal apiKey or a
configured apiKeyEnv reference. A conversation on another provider's
model keeps web_search and web_fetch while a DeepSeek key is configured,
as on master, so the route refusal WEB_PROVIDER_ROUTE_DISABLED and its
upgrade guide are removed.

The pi-ai-conversation-updates and deepseek-messages-system-prompt
snapshots replay their master recordings again, the headless keyless
smoke points its search key reference at an unset variable, and the
readiness Agent Note records the rejected route and endpoint rules.
2026-10-22 15:38:16 +08:00
Yichen Jiang a19aabc8ba feat(web): let a key of search's own serve other model routes
A user who gives DeepSeek search a key of its own has chosen what that key
pays for, so the route check now applies only to the shared
DEEPSEEK_API_KEY. A literal apiKey, or an apiKeyEnv other than
DEEPSEEK_API_KEY, opens every model route for Messages search and for the
Search and Fetch APIs; a configured endpoint does not, since the search
still sends the shared key. The Web search settings page saves its key
under the section's apiKeyEnv, DEEPSEEK_API_KEY by default, so a key saved
there keeps serving only the DeepSeek routes.

The route refusal now names the settings that give search a key of its
own, and the READMEs, the readiness Agent Note, and the upgrade guide
describe the exception.
2026-10-22 15:00:39 +08:00
Yichen Jiang 1922f322aa fix(web): gate the DeepSeek web providers on the model route alone
Review found two faults in the endpoint and configuration checks. The
providers took the official chat route's endpoint from the first active
dsh-llm-deepseek-api-key Loader row, so a composition that isolates several
instances answered for an unrelated one: a correct route lost search, or a
gateway's key reached DeepSeek. And a key saved on the Web search settings
page lands under the default DEEPSEEK_API_KEY reference, so the inference
that search had its own configuration never saw it.

Both DeepSeek web packages now serve only the deepseek-account and
deepseek-official routes, whatever key they are configured with, and no
longer compare the chat endpoint with their own. ready() checks the route
and then credential presence as before; the API-key resolver repeats the
route check and refuses with WEB_PROVIDER_ROUTE_DISABLED. The packages no
longer read Loader rows or depend on dsh-llm-deepseek-api-key, so the
lockfile, the module graph, and the keyless smoke expectations that the
endpoint check had changed return to master's.

An upgrade guide covers conversations on other providers' models, which
lose DeepSeek web search and, with the Search and Fetch APIs selected,
web_fetch.
2026-10-22 13:48:53 +08:00
Yichen Jiang 103dac1b3c feat(web): gate web_fetch and the DeepSeek web APIs on readiness
The Web search switch selects the DeepSeek Search and Fetch APIs together,
and both reuse the DeepSeek chat key by default. Neither provider answered
readiness, so a step without a usable key was offered web_search and
web_fetch whose every call failed, and a conversation on another provider's
model or behind a gateway sent the chat key to DeepSeek.

dsh-web gives WebFetchProvider an optional ready(llmProvider) and adds
ctx.web.fetchReady(), which asks only the provider fetch() selects, as
searchReady() does for search. tool-web registers web_fetch with a step
activation condition on fetchReady(), so a step that cannot fetch leaves the
tool out of declarations, guidance, and PTC bindings and refuses a call to
it as UNKNOWN_TOOL; without web_fetch the search guidance uses its
search-only text. The shipped HTTP fetch provider has no ready() and stays
admitted.

web-deepseek-api applies the API-key rules of Messages search to both APIs:
without a literal apiKey, an apiKeyEnv other than DEEPSEEK_API_KEY, or a
baseURL other than the default, the chat key goes only to the DeepSeek
routes and only to an API base on the official chat route's origin. The
refusal sits in the key's only resolver, and both providers' ready()
answers presence in the order an operation resolves credentials. The
transport repeats these checks so the package still does not depend on
dsh-web-search-deepseek.
2026-10-22 12:10:21 +08:00
Yichen Jiang 80eca619ef refactor(web-search): admit web_search through step activation conditions
Master's step-scoped conditional activation resolves each tool's condition
before a model step assembles, and both the declarations and dispatch read
that admitted set. Prompt assembly no longer carries tool declarations, so
tool-web's system-prompt/assemble listener could not remove web_search any
more, and removing a declaration there never stopped a repeated call from
running.

tool-web now registers web_search with a when condition that asks
ctx.web.searchReady() about the route of the step's model selection, and its
guidance section renders from the step's admitted set. A step that does not
admit the tool leaves it out of declarations, guidance, and PTC program
bindings, and refuses a call to it as UNKNOWN_TOOL without reaching a
provider. Tests that execute web_search directly now run inside a resolved
step, as the executor requires for conditional registrations.

Docs, the readiness Agent Note, and the event producer/consumer table follow
the new mechanism; the tools README gains an explicit conditional-tools
anchor for cross-locale links.
2026-10-22 10:57:09 +08:00
Yichen Jiang d373778fd8 Merge remote-tracking branch 'origin/master' into worktree/web-search-dynamic-tool-e26c2b
# Conflicts:
#	packages/web/tool-web/tests/tool-web.spec.ts
#	snapshots/session/pi-ai-conversation-updates/session.v4.jsonl
2026-10-22 10:32:41 +08:00
_Kerman d98025a089 Merge origin/master into xtr/tool-executor-plugins 2026-10-21 18:01:55 +08:00
07akioni ed783066e6 fix(i18n): complete Japanese plugin metadata and guard locale coverage 2026-10-21 16:14:12 +08:00
Yichen Jiang 2548bb4acf test(web-search): adapt fixtures to master's model selection and projections
Master moved the step's model route into the assembly context: the agent loop
fixes it per step and registers the prompt's provider variable from it, and
installModelSelection no longer takes a mutable selection. The tool-web
readiness fixture now registers that variable and passes the step's
modelSelection to each assembly.

AgentRegistry now needs the session projection registry, so the
web-search-deepseek initiator fixtures and the Loader composition in
chat-endpoint.spec.ts mount it, as master's own fixtures in this package do.
2026-10-21 14:53:05 +08:00
Yichen Jiang 7a75f225c5 Merge remote-tracking branch 'origin/master' into worktree/web-search-dynamic-tool-e26c2b
# Conflicts:
#	apps/cli/tests/profiles/headless/tests/fixtures/cli.patch.yml
#	apps/cli/tests/profiles/sdk/keyless-smoke.e2e.ts
#	apps/web/tests/expected/plugin-config/official.expected.md
#	apps/web/tests/expected/plugin-manager/live-enabled.expected.md
#	apps/web/tests/expected/plugin-manager/manager.expected.md
#	apps/web/tests/expected/plugin-manager/refresh.expected.md
#	apps/web/tests/plugin-config.e2e.ts
#	apps/web/tests/plugin-manager.e2e.ts
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	docs/module-graph.md
#	docs/module-graph.zh.md
#	docs/subsystems/web.i18n.yaml
#	docs/subsystems/web.md
#	docs/subsystems/web.zh.md
#	packages/client/ui-settings-web-search/README.i18n.yaml
#	packages/client/ui-settings-web-search/README.md
#	packages/client/ui-settings-web-search/README.zh.md
#	packages/web/tool-web/README.i18n.yaml
#	packages/web/tool-web/README.md
#	packages/web/tool-web/README.zh.md
#	packages/web/web-search-deepseek/README.i18n.yaml
#	packages/web/web-search-deepseek/README.md
#	packages/web/web-search-deepseek/README.zh.md
#	packages/web/web-search-deepseek/src/provider.ts
#	packages/web/web-search-deepseek/tests/deepseek.spec.ts
#	packages/web/web/src/index.ts
#	snapshots/session/web-search-endpoint-guidance/session.v4.jsonl
2026-10-21 14:47:47 +08:00
_Kerman 38b9a095f3 Merge remote-tracking branch 'origin/master' into xtr/tool-executor-plugins
# Conflicts:
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	packages/web/tool-web/tests/integration.spec.ts
#	pnpm-lock.yaml
#	scripts/check-workspace-constraints.spec.ts
#	scripts/check-workspace-constraints.ts
2026-10-21 11:22:06 +08:00
Turtle c3e9b28845 Merge pull request #6354 from deepseek-harness/agent/web-fetch-fakeip-469be4db70d0
fix(web)!: let web_fetch reach non-public destinations
2026-10-20 14:13:24 +08:00
yudshj ce79846fe6 refactor(web): keep search summary default internal 2026-10-20 12:46:14 +08:00
yudshj e715991ab9 feat(web): give the model Search API summaries
The DeepSeek Search API returns a short `snippet` for display and a longer
`summary` written for model reading. Carry `summary` on `WebSearchSource`,
map it from the Search API, and render it in place of the snippet in
`web_search` output while the search card keeps the snippet.

`tool-web` collapses summary whitespace and caps each summary at the new
`searchSummaryMaxChars` setting (800 characters by default, the upper end of
the guide's typical range; live summaries measured 1,000-2,300). The cut keeps
surrogate pairs intact and ends in an ellipsis. The PTC tool output type gains
the optional field, so the affected system-prompt sidecars are refreshed, and
the web search round now exercises a multi-line summary.
2026-10-20 12:46:14 +08:00
Turtleandturtle1999 2f01ad549c fix(web)!: let web_fetch reach non-public destinations
The HTTP fetch provider resolved each hostname, rejected any non-public
answer, and pinned the connection. That refused every page behind
TUN-mode proxies with fake-IP DNS and did not confine a model that has
shell network access. Requests now go through the installed proxy
dispatcher and the system resolver with no address restriction.

HttpFetchResolver and the HttpFetchProvider resolver parameter are
removed; the upgrade guide covers the migration.

Co-authored-by: turtle1999 <[email protected]>
2026-10-20 12:44:14 +08:00
_Kerman e9784c073b fix(tools): settle executor calls after unload and validate configuration 2026-10-19 16:32:29 +08:00
_Kerman 3b978adcec test(tools): tidy migrated fixture imports and comments 2026-10-19 16:32:04 +08:00
_Kerman 31a49c6629 Merge remote-tracking branch 'origin/master' into xtr/tool-executor-plugins
# Conflicts:
#	docs/capability-seams.i18n.yaml
#	docs/tool-catalog.i18n.yaml
#	packages/bundle/base/package.json
#	packages/core/agent-loop/tests/serial-listener-review.spec.ts
#	packages/core/agent-tool-presentation/package.json
#	packages/experimental/auto-review/tests/auto-review.spec.ts
#	packages/lsp/tool-lsp/package.json
#	packages/subagent/tool-subagent/tests/list-models.spec.ts
#	scripts/check-workspace-constraints.ts
#	scripts/type-equiv.manifest.json
#	tsconfig.base.json
2026-10-19 14:53:33 +08:00
_Kerman 455686626d refactor(tools): separate scoped executors from tool registration 2026-10-19 14:49:55 +08:00
imccyu d1b9c04067 release(dsh): 0.2.1-rc.2 2026-10-17 15:10:31 +08:00
Yudong HanandLegGasai fda98dadd2 feat(web): add DeepSeek Search and Fetch APIs behind a settings switch (#6103)
* fix(web-search-deepseek): disable thinking for auxiliary search requests

The provider discards the generated text of the auxiliary Messages turn,
so reasoning tokens have no consumer. DeepSeek enables thinking by default,
which adds latency and output tokens to every search.

Fixes #6099.

* feat(web): add opt-in traditional Search and Fetch APIs

* test(web): verify API settings persistence and refresh Session evidence

* fix(web): require an explicit Search API base with its route prefix

* docs(web): document direct Search and Fetch API decisions

* fix(web): keep Messages search default behind the new Web Search API switch

- Rename useTraditionalApi/traditionalBaseURL to useSearchApi/searchApiBaseURL and
  label the switch "Use the new Web Search API"; Messages search stays the default.
- Default the Search / Fetch API base to https://api.deepseek.com/v1/tools.
- Authenticate the Search API with an API key only: DeepSeek Account tokens
  returned an empty HTTP 404 on /v1/tools/* when probed on 2026-10-16.
- Remove the count setting, which tool-web's maxResults always overrides, and
  the undocumented DeepSeek-API-Version header.
- Name the active API's base URL field and the current settings path in
  endpoint guidance; re-record the endpoint guidance Session.
- Keep WebFetchResult.statusCode required: DeepSeek Fetch reports HTTP 200 and
  drops the page title, so the fetch-metadata widening and its upgrade guide
  are removed.

* fix(web): accept account sign-in on the live Search API

The public Search and Fetch routes went live on 2026-10-16 and accept the
DeepSeek Account token, so account-route Sessions send it to the Search API
as they do for Messages search, and the settings copy no longer says account
sign-in does not apply.

Record the live behavior in the README and Agent Note: summaries of
1,000-2,300 characters, an HTTP 500 for an unresolvable Fetch target, and
migration notes for gateways without /v1/tools routes. Test doubles now use
the live response fields and RFC 3339 offsets.

* feat(web): configure Search API result count and send the triggering question

- Add web-search-deepseek.count (1-50, default 10, the service default) and
  show it as "Results per search" in Search API mode, where "Max searches per
  request" applies only to Messages; a smaller request maxResults still wins.
- Send the initiating Session's latest human prompt as ori_question, with
  whitespace collapsed and cut to the API's 128-character limit; blank or
  absent prompts omit it, and sendOriginalQuestion: false disables it. The
  settings hint states that the question is sent.
- Match the web search double on the request's query field, since
  ori_question now carries every query in the prompt.

* fix(client): unify web search endpoint override controls

* feat(web): configure registered search and fetch providers through shared settings

* feat(web): include Exa and Perplexity in provider settings

* test(web): cover provider settings branches and refresh plugin snapshots

* revert(web): drop the provider dropdown, schema-driven form, and Exa/Perplexity settings

Revert aed8c5cb609, d88c47dcd80, and 036a5a337d3. The Web search settings
page returns to the DeepSeek toggle; the DeepSeek Search and Fetch API split
is reintroduced as one plugin in the following commit.

* feat(web): move the DeepSeek Search and Fetch APIs into web-deepseek-api

The new web-deepseek-api plugin registers deepseek-search-api and
deepseek-fetch-api over one credential and API base, and exports the
authentication and redirect-rejecting transport that web-search-deepseek now
reuses for Messages search only. The web service reads searchProvider and
fetchProvider for each operation, so the Web search settings switch selects
both APIs together on save and restores Messages search with local HTTP fetch
when turned off. The switch hint links to the DeepSeek search and fetch guide.

* refactor(client): keep the web search endpoint fields on the shared override controls

Drop the always-visible reset and the inherited-address placeholder from the
Web search page, restoring ui-primitives to its base. Each endpoint field shows
the selected section's effective value and offers Reset to default only for an
override, like every other settings field.

* refactor(web): keep Messages search independent of web-deepseek-api

Restore dsh-web-search-deepseek to its own credential and transport code,
keeping only the disabled-thinking request and the current settings guidance,
so the Messages plugin no longer depends on the Search and Fetch API package.
dsh-web-deepseek-api keeps its transport internal; the regions that repeat the
Messages helpers carry jscpd:ignore markers, with the rationale recorded in the
Search and Fetch API Agent Note.

* refactor(web): keep the web-deepseek-api request type module-private

---------

Co-authored-by: LegGasai <[email protected]>
2026-10-17 15:08:08 +08:00
imccyu c6e89e6755 release(dsh): 0.2.1-rc.1 2026-10-16 21:39:18 +08:00
imccyu 0a49d91bb2 release: setup next release version for [email protected] 2026-10-16 16:17:09 +08:00
imccyu f2235d6c02 release(dsh): 0.2.1-alpha.4 2026-10-15 20:00:58 +08:00
Yichen Jiang 36acfb1e88 release(dsh): 0.2.1-alpha.3 2026-10-14 19:38:37 +08:00
ZiyaandZiyaZhang c7d61ecc11 fix(i18n): 补充内置插件中文文案并精简清单展示 (#5506)
* fix(i18n): add Chinese names and descriptions for built-in plugins

* fix(i18n): keep built-in plugin localization metadata-only

---------

Co-authored-by: ZiyaZhang <[email protected]>
2026-10-09 17:18:26 +00:00
07akioni e2c410ef84 release(dsh): 0.2.1-alpha.2 2026-10-09 15:13:40 +08:00
Yichen Jiang fdd29b45b8 refactor(web-search): open search to every route once search has its own setup
Replace the `routes` allowlist with the user's own configuration. A search
whose key or endpoint the user configured (a literal apiKey, an apiKeyEnv
other than DEEPSEEK_API_KEY, baseURL, or $DEEPSEEK_SEARCH_BASE_URL) serves
every model route without the route or endpoint check. Without such
configuration the key is the DeepSeek chat key: only the DeepSeek account and
official routes may use it, and only toward a search endpoint on the official
chat route's origin. The account token rule stays as before.

Both refusals now name the settings that give search its own setup.
2026-10-09 13:04:22 +08:00
Yichen Jiang e2b2760589 feat(web-search): send the DeepSeek API key only for DeepSeek routes and their chat origin
DeepSeek native search is served by DeepSeek's endpoint, so two checks now
gate the API key, in ready() and again in the key resolver of every search:

- Route: a model request on a route other than deepseek-account,
  deepseek-official, and the new `routes` setting is refused with
  WEB_PROVIDER_ROUTE_DISABLED, so another provider's conversation does not
  bill DeepSeek unless a deployment lists the route.
- Endpoint: the official chat route's own key reference goes only to a
  search endpoint on that route's chat origin, read from the active
  dsh-llm-deepseek-api-key Loader row with that package's resolver;
  otherwise WEB_PROVIDER_ENDPOINT_MISMATCH. A literal key, a separate key
  reference, or a search endpoint on the chat origin passes.

The provider passes a resolver's own WebError through unchanged. A real
Loader composition pins the endpoint check. The pi-ai-conversation-updates
scenario gets its own tool sidecar without web_search, and the legacy
deepseek-messages route loses web_search in deepseek-messages-system-prompt.
2026-10-09 11:14:49 +08:00
Yichen Jiang d0dda93064 Merge remote-tracking branch 'origin/master' into worktree/web-search-dynamic-tool-e26c2b
# Conflicts:
#	.agents/notes/implemented/feature/2026-07-31-web-default-search.i18n.yaml
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	packages/web/tool-web/README.i18n.yaml
#	packages/web/web-search-deepseek/README.i18n.yaml
#	packages/web/web/README.i18n.yaml
#	snapshots/sdk/subagent-dsh-sdk-dynamic-route/session.1.v4.jsonl
#	snapshots/sdk/subagent-dsh-sdk-dynamic-route/session.v4.jsonl
2026-10-08 18:56:11 +08:00
Tianyi Cui ec48669f48 release(dsh): 0.2.1-alpha.1 2026-10-03 10:46:11 +08:00
Turtle f028f25667 refactor: remove runtime invariant plugins
Delete @deepseek-ai/dsh-invariants, every package's ./invariant companion
and its tests, the invariant gates and Vitest setup, the scoped-event
resolver generator, and the INVARIANT-coded listener rethrow paths.
Update docs, READMEs, Agent Notes, and add an upgrade guide.
2026-09-30 12:52:18 +08:00
imccyu c1b47e41fc release(dsh): 0.2.0-rc.2 2026-09-29 16:40:57 +08:00
imccyu 4878cdabd8 release(dsh): 0.2.0-rc.1 (#5387) 2026-09-28 19:48:10 +08:00
Yichen Jiang 8b085b7e35 fix(web-search): check DeepSeek search readiness in the search's credential order
On the account route a search reads the account grant before any API key
and fails with WEB_PROVIDER_ERROR when that read throws; it never falls back
to the key. ready() swallowed the failed read and then accepted a literal,
stored, or environment key, so it answered true for a search that fails.

ready() now checks the account token first on the account route, answers
false when that read fails, and only then checks the literal key and the
credential reference. Tests pair the readiness answer with the real search
failure for a literal key and an environment key.
2026-09-28 12:17:36 +08:00
Yichen Jiang 7524c1ecd9 refactor(web): split web_search readiness across the web seam roles
Move per-request web_search withholding out of the DeepSeek provider:

- dsh-web: add the optional WebSearchProvider.ready(llmProvider) and
  ctx.web.searchReady(llmProvider?), which asks only the provider that
  search() would select; a provider without ready() and a selection
  failure answer true.
- dsh-web-search-deepseek: implement ready() with the existing credential
  check and drop the assemble listener together with its system-prompt,
  tools, and tool-web dependencies.
- dsh-tool-web: remove web_search and its tool:web_search section in a
  prepended system-prompt/assemble listener when searchReady() answers
  false for the route model selection assigned to the request.

A composition that selects another search provider no longer loses
web_search when DeepSeek has no credential. The Agent Note is renamed to
2026-09-27-web-search-offered-by-provider-readiness and records the split.
2026-09-27 17:00:33 +08:00
Yichen Jiang 1fc808dbc9 feat(web-search): withhold web_search from requests without search credentials
The DeepSeek search provider now removes dsh-tool-web's web_search tool and
its prompt section from each model request whose search would have no
credential: no account token for the request's deepseek-account route and
search endpoint, no literal apiKey, and no configured apiKeyEnv credential.
A prepended system-prompt/assemble listener reads the route model selection
writes for the request, so a model switch or a newly stored key applies to
the next request through the existing dynamic tool updates. PTC
presentations and execution-time authentication are unchanged.
2026-09-27 16:00:32 +08:00
turtle1999 787b746b80 release(dsh): 0.1.7-rc.2 2026-09-24 21:24:26 +08:00
turtle1999 ab2f5d3009 refactor(tools): drop system-prompt repeats of tool definitions
Tool guidance sections keep only what the tool definitions do not say.
Apply review: reword interrupt_agent and list_agents, carry "a rejected
escalation stays denied" in the rejection error, pin
sandboxPermissionsDescription, and fix stale README and Agent Note
statements. Record the tool-definition rules in agent-experience.

Compaction threshold snapshots shift their context windows by the
370-token system-prompt estimate reduction; escalation-rejected moves to
a v4 fixture for the new rejection text.
2026-09-24 17:52:58 +08:00
turtle1999 321a6fa740 refactor(tools): trim constraint prose from tool descriptions
Delete constraints the model learns from the call result, shorten
implementation detail, and move per-parameter rules into the owning
parameter descriptions. Bash, pwsh, and fs share one
sandbox_permissions description in dsh-sandbox.

The standard preset's first-turn prompt drops from 7985 to 6551 tokens.
Compaction threshold snapshots lower their context windows by the
1548-token tool-schema estimate reduction so each trigger keeps its
position; their fixtures move to v4 generations.
2026-09-24 17:52:58 +08:00
Turtle e7def469e1 feat(i18n): key translation pairing records by heading section (#5036)
* feat(i18n): key translation pairing records by heading section

Replace the whole-file blob hashes in *.i18n.yaml with one entry per
heading section that still holds language-specific content, keyed by the
English heading-slug path. Blocks identical across the pair (generated
table rows, code blocks, shared regions) stay out of the hash, so edits to
different sections and identical regenerations merge with Git's default
text merge, including on GitHub.

Remove the dsh-translation-pairing merge driver, its resolver command and
installer wiring; gen-translation-brief recovers last-confirmed text from
commit history. Migrate all active records, archive the superseded Agent
Note, and record the replayed conflict reduction in the new one.

* test(i18n): refresh translation prompt snapshot for the terminology note

* feat(i18n): exclude only code blocks and generated regions from pairing records

Replace byte-equality masking with an explicit rule: a record hash skips
fenced code blocks and BEGIN/END GENERATED regions, the two kinds of
content the pairing gate already requires to be identical on both sides.

Move generated data into regions written into both pages:
gen-config-catalog renders both languages and the record with per-package
regions labelled by code identifiers; gen-module-graph keeps its table in a
region; gen-doc-graphs splices the event matrix regions into the Chinese
page. Add a generic region splice beside the region grammar.
2026-09-23 22:32:06 +08:00
turtle1999 a60af51e80 release(dsh): 0.1.7-rc.1 2026-09-23 20:47:45 +08:00
imccyu 10ea83bcc3 release(dsh): 0.1.7-alpha.2 2026-09-22 23:12:55 +08:00