* fix(runtime): accept profile-scoped host ids in the host permission layer
DeepSeek Harness configures recall per profile, so recall-hosts.ts reports
`deepseek-harness:<profile>` as the host id. The CLI accepts that shape and
prints it from `host list`, but the runtime rejected it twice:
- HostPermissions.supported() tested `[a-z][a-z0-9-]*`, so preview/install
answered host_permission_transport_unsupported for every Harness profile.
- HostGrantStore.validHostId() applied the same restriction, so even a
supported host could not create its installation or pass the load-time
revalidation.
A host id is an opaque label: the installation directory is a digest of it,
so a colon never becomes a path component. Both checks now allow one optional
`:<profile>` segment, matching the CLI's existing HOST_ID.
Adds an integration test that previews a real `cordis.patch.yml` install for
`deepseek-harness:desktop` and a grant-store test that the colon survives a
reload, plus a regression for malformed ids.
* fix(runtime): share one host-id grammar across CLI, permissions and grants
Review follow-up: the three layers had three different host-id rules, so the
CLI could list an id the permission layer (or the grant store) then refused.
They now import `isHostId` from `@one-memory/contracts`, which is the only
definition:
- host: 1..64 chars, alphanumeric first, lower-case letters/digits/hyphens
- optional `:<profile>`: 1..32 chars, alphanumeric first
- total <= 97, and the pattern asserts true end-of-input so a trailing
newline cannot pass
Digit-leading ids stay accepted (the CLI always took them), and the profile
segment is the only colon allowed. Boundary cases live in
`packages/contracts/src/host-id.test.ts`; the grant-store and permission tests
cover the same grammar from their side.
Packaging: contracts is a source-only workspace package, so every bundle that
now imports it for a value inlines it (`noExternal`): host-bridge entries,
the built Claude Code hook assets, and the CLI. Without that the emitted CJS
asks Node for a `.js` file that only exists as `.ts` and the MCP entry fails
at startup.
---------
Co-authored-by: Vesper <226942762+VE5PER-17@users.noreply.github.com>
Co-authored-by: Vesper <hb.ran@foxmail.com>