Commit Graph
63 Commits
Author SHA1 Message Date
ericcaiwx-star 9a2217b74a test(directory-picker-win32): use a synthetic path in the UTF-16 fixture
The U+5F00 case only needs that code unit in the buffer. A real-looking user desktop path does not belong in a public fixture.
2026-08-23 14:08:39 +08:00
ericcaiwx-star 51c242749a fix(directory-picker-native): stop truncating Win32 UTF-16 paths at U+XX00
readUtf16 treated any zero low byte as NUL, so BMP characters such as 开 (U+5F00) cut the folder-picker path in half.
2026-08-23 13:56:48 +08:00
imccyu aa6c361a97 release(dsh): 0.1.1-rc.2 2026-08-21 19:48:58 +08:00
imccyu 3ec5e8f8c4 release(dsh): 0.1.1-rc.1 2026-08-21 14:11:09 +08:00
pku-xht 84cbec28e9 Merge remote-tracking branch 'origin/master' into codex/localized-chinese-doc-links
# Conflicts:
#	.agents/notes/implemented/architecture/2026-06-11-content-block-vocabulary.i18n.yaml
#	.agents/notes/implemented/architecture/2026-06-11-content-block-vocabulary.zh.md
#	.agents/notes/implemented/architecture/2026-06-18-shared-persistence-write-coordinator.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.zh.md
#	.agents/notes/implemented/bug-fix/2026-07-29-pnpm-setup-runner-isolation.i18n.yaml
#	.agents/notes/implemented/bug-fix/2026-07-29-pnpm-setup-runner-isolation.zh.md
#	.agents/notes/implemented/bug-fix/2026-08-18-request-image-payload-bound.i18n.yaml
#	.agents/notes/implemented/bug-fix/2026-08-18-request-image-payload-bound.zh.md
#	.agents/notes/implemented/feature/2026-07-06-sandbox.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-06-sandbox.zh.md
#	.agents/notes/implemented/feature/2026-07-16-persistent-pty-sessions.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-21-cross-session-references.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-21-cross-session-references.zh.md
#	.agents/notes/implemented/feature/2026-07-22-web-multimodal-image-input-and-durable-attachments.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-22-web-multimodal-image-input-and-durable-attachments.zh.md
#	.agents/notes/implemented/feature/2026-07-27-web-subagent-conversations.i18n.yaml
#	.agents/notes/implemented/feature/2026-07-31-permission-default-for-new-sessions.i18n.yaml
#	.agents/notes/implemented/feature/2026-08-03-web-search-source-scroll.i18n.yaml
#	.agents/notes/implemented/feature/2026-08-03-web-search-source-scroll.zh.md
#	.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.i18n.yaml
#	.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.zh.md
#	.agents/notes/implemented/feature/2026-08-05-agent-teams.i18n.yaml
#	.agents/notes/implemented/feature/2026-08-05-agent-teams.zh.md
#	.agents/notes/implemented/feature/2026-08-11-workspace-sidebar-order-and-folding.i18n.yaml
#	.agents/notes/implemented/feature/2026-08-15-product-subagent-noninteractive-permissions.i18n.yaml
#	.agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.i18n.yaml
#	.agents/notes/implemented/process/2026-07-21-serial-cross-platform-ci-reference.zh.md
#	.agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.i18n.yaml
#	.agents/notes/implemented/process/2026-07-22-evidence-based-larger-hosted-runners.zh.md
#	.agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.i18n.yaml
#	.agents/notes/implemented/process/2026-07-23-portable-required-pull-request-ci.zh.md
#	.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.i18n.yaml
#	.agents/notes/implemented/testing/2026-07-24-web-gui-browser-e2e-lane.zh.md
#	.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.i18n.yaml
#	.agents/notes/implemented/testing/2026-07-30-web-browser-snapshot-ci-gate.zh.md
#	.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.i18n.yaml
#	README.i18n.yaml
#	README.zh.md
#	docs/architecture.i18n.yaml
#	docs/architecture.zh.md
#	docs/development.i18n.yaml
#	docs/development.zh.md
#	docs/persistence-catalog.i18n.yaml
#	docs/persistence-catalog.zh.md
#	docs/subsystems/README.i18n.yaml
#	docs/subsystems/README.zh.md
#	docs/subsystems/agent-team.i18n.yaml
#	docs/subsystems/agent-team.zh.md
#	docs/subsystems/client-modules.i18n.yaml
#	docs/subsystems/client-modules.zh.md
#	docs/subsystems/commands.i18n.yaml
#	docs/subsystems/commands.zh.md
#	docs/subsystems/persistence.i18n.yaml
#	docs/subsystems/persistence.zh.md
#	docs/subsystems/session-reference.i18n.yaml
#	docs/tool-catalog.i18n.yaml
#	docs/tool-catalog.zh.md
#	docs/user/guide/providers.i18n.yaml
#	docs/user/guide/providers.zh.md
#	packages/README.i18n.yaml
#	packages/README.zh.md
#	packages/bundle/web-app/README.i18n.yaml
#	packages/bundle/web-app/README.zh.md
#	packages/client/README.i18n.yaml
#	packages/client/README.zh.md
#	packages/client/connection/README.i18n.yaml
#	packages/client/connection/README.zh.md
#	packages/client/ui-conversation/README.i18n.yaml
#	packages/client/ui-conversation/README.zh.md
#	packages/client/ui-primitives/README.i18n.yaml
#	packages/client/ui-primitives/README.zh.md
#	packages/client/ui-sidebar/README.i18n.yaml
#	packages/client/ui-sidebar/README.zh.md
#	packages/client/ui-workspace/README.i18n.yaml
#	packages/client/ui-workspace/README.zh.md
#	packages/context/README.i18n.yaml
#	packages/context/README.zh.md
#	packages/core/agent-loop/README.i18n.yaml
#	packages/credentials/README.i18n.yaml
#	packages/credentials/README.zh.md
#	packages/experimental/agent-team/README.i18n.yaml
#	packages/experimental/agent-team/README.zh.md
#	packages/experimental/tool-agent-team/README.i18n.yaml
#	packages/experimental/tool-agent-team/README.zh.md
#	packages/host/frontend-static/README.i18n.yaml
#	packages/host/frontend-static/README.zh.md
#	packages/host/webserver/README.i18n.yaml
#	packages/host/webserver/README.zh.md
#	packages/interaction/commands/README.i18n.yaml
#	packages/interaction/commands/README.zh.md
#	packages/plan/plan-mode/README.i18n.yaml
#	packages/plan/plan-mode/README.zh.md
#	packages/sandbox/sandbox-local/README.i18n.yaml
#	packages/sandbox/sandbox-local/README.zh.md
#	packages/session/README.i18n.yaml
#	packages/session/README.zh.md
#	packages/session/session-persistence-sqlite/README.i18n.yaml
#	packages/session/session-persistence-sqlite/README.zh.md
#	packages/session/session-projection-cache/README.i18n.yaml
#	packages/session/session-projection-cache/README.zh.md
#	packages/shell/tool-pwsh/README.i18n.yaml
#	packages/shell/tool-pwsh/README.zh.md
#	packages/subagent/subagent-codex/README.i18n.yaml
#	packages/subagent/subagent-codex/README.zh.md
#	packages/subagent/subagent/README.i18n.yaml
#	packages/subagent/subagent/README.zh.md
#	packages/web/tool-web/README.i18n.yaml
#	packages/web/tool-web/README.zh.md
#	scripts/snapshots/translation-prompt-v4/request-response.expected.json
2026-08-20 19:15:33 +08:00
imccyu f1f7dc36fa release(dsh): 0.1.0-rc.8 2026-08-19 23:00:28 +08:00
pku-xht 8d3674695b docs(i18n): localize Chinese internal links 2026-08-18 19:00:37 +08:00
imccyu bb4ca698d6 release(dsh): 0.1.0-rc.7 2026-08-17 18:47:02 +08:00
imccyu 15148dbd9a release(dsh): 0.1.0-rc.6 2026-08-13 19:52:05 +08:00
imccyu abe560f81e release(dsh): 0.1.0-rc.5 2026-08-13 18:49:33 +08:00
imccyu 8c1e8d9890 build(release): publish the dsh family publicly
Every release member now declares publishConfig.access: public, so the scope no
longer mixes levels: the 221 packages/*/* and apps/* manifests join the vendored
framework and the native packages.

check-workspace-constraints drops the per-sequence expectation and holds every
release member to public, which is what stops a member from drifting back.

Access is a property of the package, not of a version: the dsh packages already
published as restricted become world-readable at their next publication.
2026-08-13 18:49:32 +08:00
imccyu 8a954b2eca release(dsh): 0.1.0-rc.3 2026-08-13 18:39:06 +08:00
imccyu 60b04b6ef7 release(dsh): 0.1.0-rc.2 2026-08-13 17:19:37 +08:00
imccyu 22ab3beac1 release(dsh): 0.1.0-rc.1 2026-08-13 16:22:33 +08:00
Tianyi Cui c905c4694e Adopt MIT for DSH packages 2026-08-13 13:07:24 +08:00
imccyu 3e8a1cfa33 release(dsh): 0.0.1-rc.5 2026-08-13 06:14:27 +08:00
imccyu a90d9af1b2 release(dsh): 0.0.1-rc.4 2026-08-13 05:16:02 +08:00
imccyu 1e99f20963 release(dsh): 0.0.1-rc.3 2026-08-13 03:24:38 +08:00
Tianyi Cui a2d0f7f411 refactor: apply repository naming contract
Apply the accepted pre-release package, service, type, directory, and role renames as one repository-wide change.
2026-08-13 00:54:38 +08:00
imccyu 40af20cafe refactor(picker): split the directory-picker faces into their own packages
The browse and native backends were dual-face packages: a Node backend plus a
browser surface under one tsconfig that referenced Client packages. That put
Client projects — and through them the Client runtime — inside the Host
compiler aggregate, which builds before the generated Remote contributions
exist. Each browser half moves to its own Client package, and both backends
become Node-only.

The interaction is still one choice: the adaptive chooser mounts the backend
and its surface as a pair of Loader entries and tears both down in reverse, so
a resolved kind still swaps both faces. Compositions that pin an interaction
directly now pin the pair, and the chooser's runtime-string package list keeps
naming everything a composing app must resolve.
2026-08-11 23:33:16 +08:00
imccyu 5ca7be5dcb release(dsh): 0.0.1-rc.2 2026-08-11 22:52:39 +08:00
imccyu b64c3ac1ba release(dsh): 0.0.1-rc.1 2026-08-11 03:20:36 +08:00
imccyu 2c85c484d3 build(release): reference workspace members through the workspace protocol
1504 hand-written ranges pointing at workspace members become workspace:^, so
pnpm pack substitutes each member's real version at publication: sibling
peerDependencies follow the family version instead of being pinned at ^0.0.1,
and a reference to a vendored package follows that package's own line. Without
this, publishing 0.0.2 ships peer ranges naming a version that does not exist,
and 0.0.1-rc.1 does not satisfy ^0.0.1 either.

It also retires ranges that had gone stale against the workspace: ^4.0.0-rc.6
for a 4.0.0-rc.7 checkout, ^3.17.0 for schemastery 3.18.0.

workspace:* stays where an exact published version is the point, which is how
the Landlock entry pins its platform packages.

A workspace constraint now requires the protocol, so a new package cannot
reintroduce a hand-written range. The same constraint caught packages/boot/cmdline
arriving on master without the publishable trio, which this change completes.
2026-08-11 00:17:09 +08:00
imccyu 97eb14a007 build(release): make the release set publishable under the private scope
Every package under packages/, apps/, and vendor/ drops "private": true and
declares publishConfig.access "restricted": the repository now states which
packages it publishes instead of deciding it at publish time. Each one also
declares its repository and directory, which is how a consumer of a private
package reaches its source.

The Landlock packages move to restricted with them. They have never been
published, so nothing anonymous depends on them today, and the whole
@deepseek-ai scope stays private.

The workspace constraint that required every package to be private now applies
to non-members only, and asserts the publishable trio on each release member.
2026-08-11 00:09:31 +08:00
imccyu ec601ca13d build(vendor): rescope the vendored Cordis packages into @deepseek-ai
Machine-produced by `pnpm run rescope-vendor --apply` plus the regeneration it
prints: `pnpm install` for the lockfile, `pnpm run gen-third-party-notices`,
`verify-translation-pairing --write` for the touched bilingual pairs,
`gen-doc-graphs`, and one typert snapshot whose ids embed character offsets.
`pnpm run rescope-vendor --check` verifies the result.

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

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

Two rules read `@deepseek-ai/` as "another workspace plugin": the client bundle
purity gate now names the vendored libraries a browser bundle inlines, and the
files where a bare `cordis` is an agent-preset id keep that product data.
2026-08-10 22:04:13 +08:00
imccyu 717792b631 refactor: nest client manifest metadata under dsh 2026-08-10 21:11:09 +08:00
Tianyi Cui ee7458b2df docs: fix review findings — residual ordinals, hypothetical IPC row, citation-rule amendments
Purge the residual task/phase ordinals review caught ((T2), (queue cut 1)
×3, T6, P-I, 'this cut' ×6 including battery-expansion finds in two
untouched notes and a workflow JSDoc); drop the 'externally logged as
Decision 21' parenthetical and rename the remaining bare decision-21
mentions; mark the gui-layering IPC bridge row as a hypothetical example;
restore the GATE_NOTICE doc-typecheck clause and the py-types 'measured'
provenance; say subprocess-backed in filesystem.md; add the picker note
path. Amend the citations note: merged-PR evidence anchors in Agent Notes
are sanctioned, and the candidate gate patterns gain the four shapes the
sweep missed. All nine touched pairs re-recorded.
2026-08-09 21:10:59 +08:00
Tianyi Cui 25dcd7293c docs: purge chain-of-thought leakage from prose
Delete design-session citations (decision/audit/plan ordinals, stack
positions), change narration, review choreography, and reviewer-addressed
justification from comments, JSDoc, docs, READMEs, Agent Notes, tests, and
generator templates; restate every affected fact as current-state contract
prose. Fix generated docs at their sources and regenerate the catalogs and
cordis-surface regions; re-paste type-equiv blocks; update every bilingual
counterpart and re-record the pairs. Record the citation rule in the
committed-artifact-citations Agent Note.
2026-08-09 21:10:59 +08:00
Turtle dda02250f5 docs: reserve seam for complete capabilities 2026-08-09 17:26:57 +08:00
xjt 6607775604 fix(docs): close final translation review gaps 2026-08-09 13:46:14 +08:00
xjt 7ff0cbcb7e docs: complete Chinese proofreading and generated reference pairing 2026-08-09 11:02:16 +08:00
imccyu d85d0806cd fix(build): align Client build metadata 2026-08-08 12:02:58 +08:00
imccyu 9c3d5725a5 build: order Host and Client compilation faces 2026-08-08 04:20:39 +08:00
imccyu 86965b053c feat(client): inject slot declaration lifetimes 2026-08-06 11:28:46 +08:00
Turtle c991a7cc79 docs: finish hierarchy rescan after rebase 2026-08-05 16:18:58 +08:00
Turtle 8af3babaea docs: rescan rebased documentation hierarchy 2026-08-05 16:16:57 +08:00
imccyu ca27512529 fix(packages): omit source publication payloads 2026-08-05 01:15:19 +08:00
Huanqi Cao 00621f92d2 fix(picker): keep the worker-boundary mock off the vitest IPC channel
The mocked process.send consumed vitest's own fork-pool IPC messages and immediately ran the post callback, whose disconnect() severed the test worker's channel (process.connected is true under forks) — the whole spec's results vanished and win32-dialog-bindings.ts/win32-dialog-worker.ts fell to near-zero coverage on CI. The mock now records without invoking the callback or disconnecting; the real close lifecycle stays with built-worker.e2e.ts. Verified under both the threads and forks pools.
2026-08-05 00:31:43 +08:00
Huanqi Cao 4cb5f328bb refactor(picker): drop the Windows PowerShell fallback chain
The win32 tier is exactly the koffi IFileOpenDialog child process; any failure surfaces as-is. The pwsh -> Windows PowerShell 5.1 cascade, the shared WinForms script, and the triple-miss AggregateError are deleted: koffi is a packaged dependency whose availability the install guarantees, so no mechanism fallback exists (the browse backend remains the fallback at the composition level). The pwsh-first DPI picker-fix note is consolidated into a new simplification note recording the reversal.
2026-08-05 00:31:43 +08:00
Huanqi Cao 4201eaed3f refactor(picker): drive the Win32 dialog from a spawned child process
The koffi IFileOpenDialog conversation runs in a spawned child process instead of a worker thread: the dialog is the child's first window, so Windows activates it without a foreground call, and a native fault stays contained to the child. The driver maps the child's message protocol onto a promise and services aborts by posting WM_CLOSE to the dialog thread's windows, killing the child when the close budget is exhausted. The built worker ships as lib/worker.cjs (the ./worker export) under plain node, and win32-dialog.spec.ts returns to the thread-safe pool.
2026-08-05 00:31:43 +08:00
Huanqi Cao bc9171337a fix(picker): raise the worker-thread dialog to the foreground on showing
The koffi redesign moved the dialog from a spawned child process (which
inherits a foreground-activation right from the spawning app) onto a worker
thread of the same process, so Windows shows the dialog without activating
it — it opens behind the app with a taskbar flash. The app has no native
HWND to hand the seam, so raise from the driver: on the 'showing' notice
(the worker posts it right before Show, before the dialog window exists),
attach this thread's input queue to the dialog thread's, call
SetForegroundWindow on its top-level window, and detach — retried on the
close cadence until the window appears, stopped on settle/abort/success,
never blocking the pick. Injectable seam mirrors closeThreadWindows; driver
tests pin the raise and its retry; the in-process note records the
mechanism (both languages, pairing re-recorded).
2026-08-05 00:31:43 +08:00
Huanqi Cao f4095ee3eb fix(picker): end the closed worker-message switch in assertNever
The driver's switch over Win32DialogWorkerMessage handled all three current
kinds but had no default, so a fourth kind added to the worker protocol
would compile cleanly and silently no-op — settle() never called and the
pick dangles until worker exit. Add the local assertNever backstop (the
command-compact shape; this package does not depend on dsh-llm for the
helper) and the return the error case needs to avoid falling into it.
Round-five review finding.
2026-08-05 00:31:43 +08:00
Huanqi Cao 020ce50414 fix(picker): correct crash-isolation and DPI claims, wire the built-worker guard, and tidy round-four nits
The round-four review's v6 pass found three factual gaps and the v5 pass two
nits. Correct them before merge:

- The in-process note claimed a koffi signature mistake is 'contained to
  the worker thread' — worker_threads share the process, so a native access
  violation takes down the whole Node process with no PowerShell fallback.
  State the real blast radius and record the deferred pkg-VFS worker-spawn
  arm in Consequences (both languages, pairing re-recorded).
- The 2026-07-27 picker note claimed unconditional 'per-monitor-v2 DPI';
  PMv2-less hosts (Server 2016 / Win10 1607) cascade to per-monitor or
  system-aware. Say 'the best thread DPI awareness the host accepts'
  (both languages, pairing re-recorded).
- built-worker.e2e.ts was not in any keyless gate (vitest.e2e config is
  not part of the default unit run and builtBinSmokeGate's explicit list
  missed it), so lib/worker.cjs load regressions passed keyless CI. Add it
  to builtBinSmokeGate alongside the workflow-workerthread sibling.
- Remove the dead trailing 'void bindings' in win32-dialog-logic.spec.ts
  and give native-picker.spec.ts the sibling module header it lacked.
2026-08-05 00:31:43 +08:00
Huanqi Cao e234a3a274 docs(host-directory-picker-native): describe the in-process IFileOpenDialog primary in the module header
The @module header still described the Windows adapter as the pre-PR 'STA
PowerShell FolderBrowserDialog' while the README and Agent Notes document
the koffi IFileOpenDialog primary with the PowerShell chain as fallback;
mirror the README's platform summary.
2026-08-05 00:31:43 +08:00
Huanqi Cao e182f03230 fix(picker): cascade thread DPI contexts and harden the round-three review points
- setThreadDpiAwareness checks SetThreadDpiAwarenessContext's return value
  and cascades per-monitor-v2 -> per-monitor -> system-aware; DPI stays a
  deliberate cosmetic best-effort - a host accepting none (or lacking the
  API, pre-1607) still gets the modern dialog instead of a downgrade to the
  legacy fallback chain over a cosmetic concern.
- The mocked-koffi world now uses a distinctive 4-byte pointer width and
  rejects mis-sized out-buffers and mis-divided vtable offsets, so a
  regression to hardcoded 8s fails the suite (the ia32 bug class).
- A keyless built-worker e2e guard loads lib/worker.cjs under plain
  worker_threads on POSIX (the workflow-workerthread shape).
- The 'loaded lazily' module claims are reworded to attribute laziness to
  the dynamic import('koffi') calls, and the discarded close-attempt
  rejection is named at its catch.
2026-08-05 00:31:43 +08:00
Huanqi Cao 8500a21658 fix(picker): pointer-width vtable offsets, COM apartment pairing, unconditional abort budget, and the full failure chain
Review round two on the in-process dialog:

- Vtable slots and out-pointers use koffi.sizeof('void *') instead of a
  hardcoded 8 - win32-ia32 (which Node and koffi both ship) would have read
  method pointers from the wrong address and crashed in-process before any
  fallback could run.
- runFolderDialog pairs every successful (incl. S_FALSE) CoInitializeEx
  with CoUninitialize in the outermost finally, releasing the dialog first;
  a failed init is deliberately unpaired. Pinned across fake-bindings and
  mocked-koffi suites.
- The abort close budget starts unconditionally: a worker hung before the
  showing notice (koffi import or COM init) now ends in terminate instead
  of a dangling promise; WM_CLOSE posting still waits for the thread id.
- A triple miss (dialog + pwsh + 5.1) surfaces an AggregateError carrying
  all three causes - the in-process tier's reason was previously
  unrecoverable from the final PowerShell error.
- The stray '=>{ ' formatter artifacts are normalized to real blocks.

Both stale note claims from the review are fixed: the DPI note's
Consequences no longer claims an ENOENT classification or zero new
dependencies, and the 2026-07-27 picker note's Windows bullet now names
the in-process primary and keeps the PowerShell chain as fallback (both
languages, pairings re-recorded).
2026-08-05 00:31:43 +08:00
Huanqi Cao fed3149ac4 fix(picker): ship the dialog worker as the constrained ./worker artifact
The workspace files constraint keys worker bundles on the ./worker export
(lib/worker.cjs, the workflow-workerthread shape); the descriptive source
entry stays win32-dialog-worker.ts and tsdown renames the bundle.
2026-08-05 00:31:43 +08:00
Huanqi Cao 8e0880e303 test(picker): attach abort expectations before driving the close-budget race
On a fast host the 1ms close budget can exhaust and reject between waitFor
ticks; a rejection with no listener yet counted as an unhandled error in the
Linux run.
2026-08-05 00:31:43 +08:00
Huanqi Cao 089f4dfad8 feat(picker): open the Win32 folder dialog in-process over koffi
The modern IFileOpenDialog becomes the primary win32 tier: a koffi-driven
COM conversation on a worker_threads worker (the modal Show never blocks
the host event loop), per-monitor-v2 DPI via SetThreadDpiAwarenessContext,
and abort service by re-posting WM_CLOSE to the dialog thread's windows,
with terminate+unref as the last resort (Node cannot interrupt a thread
blocked in native code, and such a worker must never hold the process open).

The PowerShell chain stays as the fallback tier with its trigger widened
from ENOENT to any pwsh failure, closing the review-flagged PowerShell 6
regression (no WinForms: exit 1, not ENOENT, so 5.1 never ran).

Layering keeps per-file coverage honest on every host: pure sequencing and
the driver test against fakes anywhere; the bindings run against a mocked
koffi COM world (the session-persistence-jsonl technique); POSIX hosts
drive the real spawn plumbing to its koffi-load rejection; win32 hosts run
a real open-and-abort-close smoke. The smoke joins processBoundTests: a
worker blocked in a native modal wedges the threads pool's teardown, while
a fork contains it. The worker bundles as its own CJS tsdown entry
(workflow-workerthread's pattern; no TLA), and the host module is imported
statically so the node-half bundle stays chunk-free.

Built-plane and real-COM behavior verified on native Windows: standalone
probes for the source worker, the built CJS worker, and the driver's abort
path all open and close the real dialog.

Agent Notes: new implemented/feature/2026-08-02-win32-in-process-folder-dialog
(bilingual) owns the decision; the DPI note is re-scoped to the fallback tier
it now describes and its AutoUpgradeEnabled attribution corrected (.NET Core
3.0 rewrote FolderBrowserDialog; the opt-out arrived in .NET 6).
2026-08-05 00:31:43 +08:00
Huanqi Cao da1b1ff87d fix(host): drop the folder-dialog Description both picker modes render badly
.NET 10's modern FolderBrowserDialog renders Description as a bottom
strip above the folder input, and the 5.1 classic dialog as an unthemed
white box; the property is dropped entirely and a regression assertion
pins its absence.
2026-08-05 00:31:43 +08:00