mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
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.
This commit is contained in:
@@ -96,7 +96,7 @@ Real-API tests and demos read `DEEPSEEK_API_KEY`, optional `DEEPSEEK_BASE_URL`,
|
||||
|
||||
## Conventions
|
||||
|
||||
- Every npm package is `@deepseek-ai/dsh-<name>`; vendored packages keep upstream names and are `private: true`. `cordis` is a peerDependency (+ dev) of every harness package.
|
||||
- Every npm package is `@deepseek-ai/dsh-<name>`; vendored packages are rescoped ([mapping](docs/rescope.md)) and `private: true`. `@deepseek-ai/cordis` is a peerDependency (+ dev) of every harness package.
|
||||
- ESM everywhere (`"type": "module"`). Use package names across packages and `.ts` in local relative imports. Config subprocesses run built `lib/` under plain Node; source regressions use their declared launcher ([testing policy](docs/testing.md#test-subprocess-launch-modes)). The `dsh` CLI source launch runs through tsx's ESM-only hook (`node --import tsx/esm`); modules it reaches must stay ESM (no CJS-only exports) — Node's native TypeScript modes are unavailable across the engines range ([source-launch contract](.agents/notes/implemented/architecture/2026-07-29-dsh-source-launch-tsx-esm.md)). Raw/Web `cordis.yml` bare plugins must appear in their resolver manifest's `dependencies`; `verify-cordis-config` enforces it.
|
||||
- **Registrations are effects**: every contribution goes through `ctx.effect()` / `ctx.on()`; a registry's `register()` returns the disposer.
|
||||
- **Runtime invariants assert owned relationships.** Check authoritative event streams or mutable data, not service or method presence, plugin metadata or effects, or fixed pure examples. Without a plausible relationship, an explained empty companion is correct ([package invariant rules](packages/AGENTS.md)).
|
||||
|
||||
@@ -11,19 +11,19 @@ The complete npm transitive closure, including the Landlock launcher workspace,
|
||||
|
||||
## Vendored source (`vendor/`)
|
||||
|
||||
The Cordis framework and its foundation libraries are source-vendored into this repository rather than consumed from npm. All are MIT-licensed; each directory preserves its upstream `LICENSE` file. Exact upstream commits and local modifications are recorded in [`vendor/README.md`](vendor/README.md).
|
||||
The Cordis framework and its foundation libraries are source-vendored into this repository rather than consumed from npm, and republished under the `@deepseek-ai` scope. All are MIT-licensed; each directory preserves its upstream `LICENSE` file. Exact upstream commits and local modifications are recorded in [`vendor/README.md`](vendor/README.md).
|
||||
|
||||
| Package | Upstream | License |
|
||||
| --- | --- | --- |
|
||||
| `cosmokit` | [github.com/deepseek-harness/cosmokit](https://github.com/deepseek-harness/cosmokit) | MIT |
|
||||
| `schemastery` | [github.com/deepseek-harness/schemastery](https://github.com/deepseek-harness/schemastery) | MIT |
|
||||
| `cordis` | [github.com/cordiverse/cordis](https://github.com/cordiverse/cordis) | MIT |
|
||||
| `@cordisjs/plugin-loader` | [github.com/cordiverse/cordis](https://github.com/cordiverse/cordis) | MIT |
|
||||
| `@cordisjs/plugin-include` | [github.com/deepseek-harness/cordis](https://github.com/deepseek-harness/cordis) | MIT |
|
||||
| `@cordisjs/plugin-group` | [github.com/deepseek-harness/cordis](https://github.com/deepseek-harness/cordis) | MIT |
|
||||
| `@cordisjs/plugin-timer` | [github.com/deepseek-harness/cordis](https://github.com/deepseek-harness/cordis) | MIT |
|
||||
| `@cordisjs/plugin-hmr` | [github.com/deepseek-harness/cordis](https://github.com/deepseek-harness/cordis) | MIT |
|
||||
| `@cordisjs/plugin-logger-console` | [github.com/deepseek-harness/cordis](https://github.com/deepseek-harness/cordis) | MIT |
|
||||
| Package | Upstream name | Upstream | License |
|
||||
| --- | --- | --- | --- |
|
||||
| `@deepseek-ai/cosmokit` | `cosmokit` | [github.com/deepseek-harness/cosmokit](https://github.com/deepseek-harness/cosmokit) | MIT |
|
||||
| `@deepseek-ai/schemastery` | `schemastery` | [github.com/deepseek-harness/schemastery](https://github.com/deepseek-harness/schemastery) | MIT |
|
||||
| `@deepseek-ai/cordis` | `cordis` | [github.com/cordiverse/cordis](https://github.com/cordiverse/cordis) | MIT |
|
||||
| `@deepseek-ai/cordis-plugin-loader` | `@cordisjs/plugin-loader` | [github.com/cordiverse/cordis](https://github.com/cordiverse/cordis) | MIT |
|
||||
| `@deepseek-ai/cordis-plugin-include` | `@cordisjs/plugin-include` | [github.com/deepseek-harness/cordis](https://github.com/deepseek-harness/cordis) | MIT |
|
||||
| `@deepseek-ai/cordis-plugin-group` | `@cordisjs/plugin-group` | [github.com/deepseek-harness/cordis](https://github.com/deepseek-harness/cordis) | MIT |
|
||||
| `@deepseek-ai/cordis-plugin-timer` | `@cordisjs/plugin-timer` | [github.com/deepseek-harness/cordis](https://github.com/deepseek-harness/cordis) | MIT |
|
||||
| `@deepseek-ai/cordis-plugin-hmr` | `@cordisjs/plugin-hmr` | [github.com/deepseek-harness/cordis](https://github.com/deepseek-harness/cordis) | MIT |
|
||||
| `@deepseek-ai/cordis-plugin-logger-console` | `@cordisjs/plugin-logger-console` | [github.com/deepseek-harness/cordis](https://github.com/deepseek-harness/cordis) | MIT |
|
||||
|
||||
## Runtime npm dependencies
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ The dsh-base bundle patch every profile applies first; mode bundles (dsh-web-app
|
||||
```mermaid
|
||||
flowchart LR
|
||||
cfg["packages/bundle/base/cordis.patch.yml<br/>cordis.yml"]
|
||||
plugin_dsh_base_timer["timer<br/>@cordisjs/plugin-timer"]
|
||||
plugin_dsh_base_timer["timer<br/>@deepseek-ai/cordis-plugin-timer"]
|
||||
cfg --> plugin_dsh_base_timer
|
||||
plugin_dsh_base_hmr["hmr<br/>@cordisjs/plugin-hmr"]
|
||||
plugin_dsh_base_hmr["hmr<br/>@deepseek-ai/cordis-plugin-hmr"]
|
||||
cfg --> plugin_dsh_base_hmr
|
||||
plugin_dsh_base_llm["llm<br/>@deepseek-ai/dsh-llm"]
|
||||
cfg --> plugin_dsh_base_llm
|
||||
@@ -164,8 +164,8 @@ flowchart LR
|
||||
|
||||
| Plugin id | Package / module |
|
||||
| --- | --- |
|
||||
| `timer` | `@cordisjs/plugin-timer` |
|
||||
| `hmr` | `@cordisjs/plugin-hmr` |
|
||||
| `timer` | `@deepseek-ai/cordis-plugin-timer` |
|
||||
| `hmr` | `@deepseek-ai/cordis-plugin-hmr` |
|
||||
| `llm` | `@deepseek-ai/dsh-llm` |
|
||||
| `session` | `@deepseek-ai/dsh-session` |
|
||||
| `typert` | `@deepseek-ai/dsh-typert-registry` |
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@cordisjs/plugin-hmr": "workspace:*",
|
||||
"@cordisjs/plugin-include": "workspace:*",
|
||||
"@cordisjs/plugin-loader": "workspace:*",
|
||||
"@cordisjs/plugin-timer": "workspace:*",
|
||||
"@deepseek-ai/cordis-plugin-hmr": "workspace:*",
|
||||
"@deepseek-ai/cordis-plugin-include": "workspace:*",
|
||||
"@deepseek-ai/cordis-plugin-loader": "workspace:*",
|
||||
"@deepseek-ai/cordis-plugin-timer": "workspace:*",
|
||||
"@deepseek-ai/dsh-agent-tool-mode": "workspace:^",
|
||||
"@deepseek-ai/dsh-app-boot": "workspace:^",
|
||||
"@deepseek-ai/dsh-base": "workspace:^",
|
||||
@@ -63,7 +63,7 @@
|
||||
"@deepseek-ai/dsh-workflow-workerthread": "workspace:^",
|
||||
"@deepseek-ai/dsh-workspace-context": "workspace:^",
|
||||
"commander": "^15.0.0",
|
||||
"cordis": "^4.0.0-rc.7",
|
||||
"@deepseek-ai/cordis": "^4.0.0-rc.7",
|
||||
"js-yaml": "^4.2.0",
|
||||
"node-addon-require-builtin": "^0.1.4"
|
||||
},
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
import { writeFileSync } from 'node:fs'
|
||||
import { join, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { FiberState, type Context } from 'cordis'
|
||||
import type { PatchOptions } from '@cordisjs/plugin-include'
|
||||
import { FiberState, type Context } from '@deepseek-ai/cordis'
|
||||
import type { PatchOptions } from '@deepseek-ai/cordis-plugin-include'
|
||||
import { dshHomePath } from '@deepseek-ai/dsh-paths'
|
||||
import {
|
||||
boot,
|
||||
@@ -308,9 +308,9 @@ export async function runProfile(options: RunProfileOptions): Promise<{ ctx: Con
|
||||
// bare custom profile may not mount either.
|
||||
if (ctx.get('hmr') === undefined) {
|
||||
if (ctx.get('timer') === undefined) {
|
||||
await ctx.loader.create({ name: '@cordisjs/plugin-timer' })
|
||||
await ctx.loader.create({ name: '@deepseek-ai/cordis-plugin-timer' })
|
||||
}
|
||||
await ctx.loader.create({ name: '@cordisjs/plugin-hmr', config: { root: [] } })
|
||||
await ctx.loader.create({ name: '@deepseek-ai/cordis-plugin-hmr', config: { root: [] } })
|
||||
}
|
||||
await watchUserPatches(ctx, {
|
||||
binName: NAME,
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
|
||||
import { networkInterfaces } from 'node:os'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import type { Context } from 'cordis'
|
||||
import type { PatchOptions } from '@cordisjs/plugin-include'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type { PatchOptions } from '@deepseek-ai/cordis-plugin-include'
|
||||
import { addHarnessSourceSection } from '@deepseek-ai/dsh-app-boot'
|
||||
import type { EnvironmentSnapshot } from '@deepseek-ai/dsh-environment'
|
||||
import { runProfile, type ProfileRows } from './profile-boot.ts'
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
import { join } from 'node:path'
|
||||
import type { PatchOptions } from '@cordisjs/plugin-include'
|
||||
import type { PatchOptions } from '@deepseek-ai/cordis-plugin-include'
|
||||
import { loadOverlayPatches, type ProfileLayer } from '@deepseek-ai/dsh-app-boot'
|
||||
|
||||
/** The base bundle whose package carries the Windows shell patch. */
|
||||
|
||||
@@ -44,8 +44,8 @@ describe('parseDshArgs', () => {
|
||||
.toEqual({ mode: 'plugin', profile: 'tui', args: ['add', 'turtle-ui'] })
|
||||
expect(parse(['plugin', '--profile', 'tui', 'remove', 'turtle-ui']))
|
||||
.toEqual({ mode: 'plugin', profile: 'tui', args: ['remove', 'turtle-ui'] })
|
||||
expect(parse(['plugin', '--profile', 'tui', 'why', 'cordis']))
|
||||
.toEqual({ mode: 'plugin', profile: 'tui', args: ['why', 'cordis'] })
|
||||
expect(parse(['plugin', '--profile', 'tui', 'why', '@deepseek-ai/cordis']))
|
||||
.toEqual({ mode: 'plugin', profile: 'tui', args: ['why', '@deepseek-ai/cordis'] })
|
||||
// Unknown pnpm flags forward verbatim.
|
||||
expect(parse(['plugin', '--profile', 'tui', 'add', '--save-dev', 'x']))
|
||||
.toEqual({ mode: 'plugin', profile: 'tui', args: ['add', '--save-dev', 'x'] })
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { Context } from 'cordis'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { agentEvents, Inbox, type Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
import { boot, loadOverlayPatches } from '@deepseek-ai/dsh-app-boot'
|
||||
|
||||
2
apps/cli/tests/fixtures/never-dispose.mjs
vendored
2
apps/cli/tests/fixtures/never-dispose.mjs
vendored
@@ -4,7 +4,7 @@ import { existsSync } from 'node:fs'
|
||||
|
||||
/**
|
||||
* Register a disposer that keeps process shutdown pending until it is forced.
|
||||
* @param {import('cordis').Context} ctx - loader-mounted test plugin context.
|
||||
* @param {import('@deepseek-ai/cordis').Context} ctx - loader-mounted test plugin context.
|
||||
*/
|
||||
export function apply(ctx) {
|
||||
const keepAlive = setInterval(() => {}, 60_000)
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import type { Context } from 'cordis'
|
||||
import type { PatchOptions } from '@cordisjs/plugin-include'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type { PatchOptions } from '@deepseek-ai/cordis-plugin-include'
|
||||
import { boot, loadOverlayPatches } from '@deepseek-ai/dsh-app-boot'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import ToolRegistry from '@deepseek-ai/dsh-tools'
|
||||
|
||||
@@ -3,11 +3,11 @@ import { mkdir, mkdtemp, readFile, stat, writeFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { Context } from 'cordis'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import { boot, healProfilesModuleFallback, loadOverlayPatches } from '@deepseek-ai/dsh-app-boot'
|
||||
import { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import type { PatchOptions } from '@cordisjs/plugin-include'
|
||||
import type { PatchOptions } from '@deepseek-ai/cordis-plugin-include'
|
||||
import { beforeAll, describe, expect, it } from 'vitest'
|
||||
import { settingsNamespace } from '@deepseek-ai/dsh-settings'
|
||||
import { resolveSessionPreset, SETTINGS_NAMESPACE } from '@deepseek-ai/dsh-agent-presets'
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cordisjs/plugin-group": "workspace:^",
|
||||
"@deepseek-ai/cordis-plugin-group": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-modules": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
|
||||
@@ -6,8 +6,8 @@ import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { chromium } from 'playwright'
|
||||
import { expect, it } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import type { Fiber } from 'cordis'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import type { Fiber } from '@deepseek-ai/cordis'
|
||||
import LocalSubprocessService from '@deepseek-ai/dsh-subprocess-local'
|
||||
import type { SubprocessHandle, SubprocessSpawnSpec } from '@deepseek-ai/dsh-subprocess'
|
||||
import { REPO_ROOT } from './support.ts'
|
||||
|
||||
@@ -29,10 +29,10 @@ import { join } from 'node:path'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import type { Page } from 'playwright'
|
||||
import { expect } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import Loader from '@cordisjs/plugin-loader'
|
||||
import Include, { type PatchOptions } from '@cordisjs/plugin-include'
|
||||
import Group from '@cordisjs/plugin-group'
|
||||
import { Context } from '@deepseek-ai/cordis'
|
||||
import Loader from '@deepseek-ai/cordis-plugin-loader'
|
||||
import Include, { type PatchOptions } from '@deepseek-ai/cordis-plugin-include'
|
||||
import Group from '@deepseek-ai/cordis-plugin-group'
|
||||
import { scrubRequestHeaders, stabilizeFixtureMessageIds } from '@deepseek-ai/dsh-acp-snapshot'
|
||||
import {
|
||||
addHarnessSourceSection,
|
||||
@@ -464,7 +464,7 @@ export async function launchWebScaffold(options: LaunchOptions = {}): Promise<We
|
||||
// `cordis:group` beside it, exactly as `boot()` registers it: a group row is
|
||||
// how a preset gives one `isolate` realm to a provider and its consumers,
|
||||
// and a preset resolving package names from its own directory cannot reach
|
||||
// `@cordisjs/plugin-group` by name.
|
||||
// `@deepseek-ai/cordis-plugin-group` by name.
|
||||
ctx.loader.builtins.group = Group
|
||||
// The shipped CLI deliberately has no dependency on this opt-in package.
|
||||
// Keep the Loader row real without broadening the product installation.
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/api-gateway.md
|
||||
api-gateway.md: 81cd80893d53212edc74cc85e3e05731fa05f411
|
||||
api-gateway.zh.md: 692cf825f619f71e86ae801e04246e9feb4a4c36
|
||||
api-gateway.md: 06f98546c8858af08efcb07c007d8e1b95b90c19
|
||||
api-gateway.zh.md: 82bf8923c45151d0c00e71848be89806427d1ce3
|
||||
|
||||
@@ -17,7 +17,7 @@ Services normally extend `GatewayService` so the constructor explicitly binds th
|
||||
```ts
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { GatewayService, Remote, RemoteScope } from '@deepseek-ai/dsh-type-meta'
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export interface CreateGoalRequest {
|
||||
objective: string
|
||||
@@ -60,7 +60,7 @@ The Client uses concrete functions on ordinary objects, not a JavaScript Proxy.
|
||||
```ts ignore-check
|
||||
import type { SessionId } from '@deepseek-ai/dsh-session/types'
|
||||
import type { AgentContext } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type {} from '@deepseek-ai/dsh-api-remotes/client'
|
||||
|
||||
export const inject = ['remote', 'remote.goals']
|
||||
|
||||
@@ -17,7 +17,7 @@ Service 通常继承 `GatewayService`,让 Cordis service key 与默认 Remote
|
||||
```ts
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { GatewayService, Remote, RemoteScope } from '@deepseek-ai/dsh-type-meta'
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export interface CreateGoalRequest {
|
||||
objective: string
|
||||
@@ -60,7 +60,7 @@ Client 使用普通对象上的具体函数,不使用 JavaScript Proxy。直
|
||||
```ts ignore-check
|
||||
import type { SessionId } from '@deepseek-ai/dsh-session/types'
|
||||
import type { AgentContext } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type {} from '@deepseek-ai/dsh-api-remotes/client'
|
||||
|
||||
export const inject = ['remote', 'remote.goals']
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/cookbook/adding-a-package.md
|
||||
adding-a-package.md: dcd5fa66f3616c2c22930babd09cb3edab38e182
|
||||
adding-a-package.zh.md: c8769197e0b1db31348b7f2442dbcd636bf43cb2
|
||||
adding-a-package.md: e108b9e88e0f0e470f96173306af79c69ff695fb
|
||||
adding-a-package.zh.md: 97b4df150fda7010fba048d7acb5e23a87519911
|
||||
|
||||
@@ -22,7 +22,7 @@ packages/<group>/<pkg>/
|
||||
|
||||
Choose an existing group when one matches the package's role (`core`, `llm`, `bash`, `compact`, `subagent`, `todo`, `session-persistence`, `ui`, `util`, or `support`). A new group is allowed, but it is a pure container: no `package.json`, no source files, and packages still sit exactly one level below it.
|
||||
|
||||
package.json invariants (enforced by `pnpm run constraints` / `scripts/check-workspace-constraints.ts`): `private: true`, a `version` matching the root `package.json`, `type: module`, `main: "lib/index.js"`, `types: "lib/types/index.d.ts"`, `exports["."].types: "./lib/types/index.d.ts"`, `exports["."].default: "./lib/index.js"`, `cordis` in BOTH peerDependencies and devDependencies (same range). Mirror every dsh peer dependency in devDependencies. `schemastery` goes in `dependencies` (it is a runtime validator), matching agent-loop. The `files` list contains exactly `lib/index.js`, `lib/invariant.js`, `lib/types/**/*.d.ts`, and package-specific runtime artifacts recognized by the gate; a package whose runtime export points into the emitted tree also includes `lib/types/**/*.js`. Do not publish `src`, declaration maps, JS maps, or stale root declaration files. CLI app packages with a package `bin` include `lib/bin.js` immediately after `lib/index.js` in `files`.
|
||||
package.json invariants (enforced by `pnpm run constraints` / `scripts/check-workspace-constraints.ts`): `private: true`, a `version` matching the root `package.json`, `type: module`, `main: "lib/index.js"`, `types: "lib/types/index.d.ts"`, `exports["."].types: "./lib/types/index.d.ts"`, `exports["."].default: "./lib/index.js"`, `@deepseek-ai/cordis` in BOTH peerDependencies and devDependencies (same range). Mirror every dsh peer dependency in devDependencies. `@deepseek-ai/schemastery` goes in `dependencies` (it is a runtime validator), matching agent-loop. The `files` list contains exactly `lib/index.js`, `lib/invariant.js`, `lib/types/**/*.d.ts`, and package-specific runtime artifacts recognized by the gate; a package whose runtime export points into the emitted tree also includes `lib/types/**/*.js`. Do not publish `src`, declaration maps, JS maps, or stale root declaration files. CLI app packages with a package `bin` include `lib/bin.js` immediately after `lib/index.js` in `files`.
|
||||
|
||||
In-package relative imports use explicit `.ts` specifiers in source (for example, `export * from './types.ts'`). The compiler rewrites those to `.js` in emitted JS and leaves explicit `.ts` specifiers in declarations, which standard NodeNext/Node16 TypeScript consumers resolve to the sibling `.d.ts` files.
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ packages/<group>/<pkg>/
|
||||
|
||||
当已有分组与包的角色匹配时,选择该分组(`core`、`llm`、`bash`、`compact`、`subagent`、`todo`、`session-persistence`、`ui`、`util` 或 `support`)。允许新建分组,但分组只是纯容器:没有 `package.json`,没有源文件,包仍然恰好位于其下一层。
|
||||
|
||||
package.json 不变式(由 `pnpm run constraints` / `scripts/check-workspace-constraints.ts` 强制执行):`private: true`,`version` 与根 `package.json` 一致,`type: module`,`main: "lib/index.js"`,`types: "lib/types/index.d.ts"`,`exports["."].types: "./lib/types/index.d.ts"`,`exports["."].default: "./lib/index.js"`,`cordis` 同时出现在 peerDependencies 和 devDependencies 中(相同范围)。每个 dsh 对等依赖(peer dependency)都要在 devDependencies 中镜像。`schemastery` 放在 `dependencies` 中(它是运行时校验器),与 agent-loop 保持一致。`files` 列表精确包含 `lib/index.js`、`lib/invariant.js`、`lib/types/**/*.d.ts` 以及门禁认可的包专用运行时产物;如果包的运行时 export 指向输出树,还要包含 `lib/types/**/*.js`。不要发布 `src`、声明映射、JS map 或陈旧的根声明文件。带有 `bin` 的 CLI 应用包在 `files` 中将 `lib/bin.js` 紧跟在 `lib/index.js` 之后。
|
||||
package.json 不变式(由 `pnpm run constraints` / `scripts/check-workspace-constraints.ts` 强制执行):`private: true`,`version` 与根 `package.json` 一致,`type: module`,`main: "lib/index.js"`,`types: "lib/types/index.d.ts"`,`exports["."].types: "./lib/types/index.d.ts"`,`exports["."].default: "./lib/index.js"`,`@deepseek-ai/cordis` 同时出现在 peerDependencies 和 devDependencies 中(相同范围)。每个 dsh 对等依赖(peer dependency)都要在 devDependencies 中镜像。`@deepseek-ai/schemastery` 放在 `dependencies` 中(它是运行时校验器),与 agent-loop 保持一致。`files` 列表精确包含 `lib/index.js`、`lib/invariant.js`、`lib/types/**/*.d.ts` 以及门禁认可的包专用运行时产物;如果包的运行时 export 指向输出树,还要包含 `lib/types/**/*.js`。不要发布 `src`、声明映射、JS map 或陈旧的根声明文件。带有 `bin` 的 CLI 应用包在 `files` 中将 `lib/bin.js` 紧跟在 `lib/index.js` 之后。
|
||||
|
||||
包内的相对导入在源码中使用显式 `.ts` 后缀(例如 `export * from './types.ts'`)。编译器在输出的 JS 中将其重写为 `.js`,在声明文件中保留显式 `.ts` 后缀;标准的 NodeNext/Node16 TypeScript 消费方会将其解析到同目录的 `.d.ts` 文件。
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/cookbook/adding-a-tool.md
|
||||
adding-a-tool.md: b030d3c3a6b7dd66b6594779345a96af3a895bd8
|
||||
adding-a-tool.zh.md: 4272a7a4571782bc213ca29de1a57d51fbd24075
|
||||
adding-a-tool.md: fa39c4b97f3c0eb739ea34d1b43ef46d11285bbf
|
||||
adding-a-tool.zh.md: ab32e90fac5539ee403a36b2dd52e60db3ad603c
|
||||
|
||||
@@ -8,7 +8,7 @@ Reference for the contracts a model-facing tool must satisfy. For an ordered fir
|
||||
|
||||
```ts
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import { defineTool } from '@deepseek-ai/dsh-tools'
|
||||
|
||||
export const name = 'my-tool'
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
```ts
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import { defineTool } from '@deepseek-ai/dsh-tools'
|
||||
|
||||
export const name = 'my-tool'
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/cookbook/adding-a-vendored-package.md
|
||||
adding-a-vendored-package.md: 724d89c1c7cd728f7123a6975b5500cd40815851
|
||||
adding-a-vendored-package.zh.md: d16ec1056431a4ac1c02d50a5ef0f0a64b67ca6d
|
||||
adding-a-vendored-package.md: 239ac27565204332559038014fabae83fc2d1057
|
||||
adding-a-vendored-package.zh.md: 2bf5c7eeaffec594dbc4d13e0aa33b12a9eccf4f
|
||||
|
||||
@@ -8,7 +8,7 @@ When the harness needs another upstream Cordis package (e.g. `@cordisjs/plugin-h
|
||||
|
||||
```
|
||||
vendor/<dir>/
|
||||
package.json # from upstream; set "private": true, keep name/exports/type
|
||||
package.json # from upstream; set "private": true, rescope the name, keep exports/type
|
||||
tsconfig.json # extends ../../tsconfig.base.json (see configuration below)
|
||||
src/ # the upstream src/ verbatim
|
||||
README.md LICENSE # if upstream ships them
|
||||
@@ -29,7 +29,7 @@ vendor/<dir>/
|
||||
}
|
||||
```
|
||||
|
||||
`package.json` invariants: `"private": true` (vendored packages are never published), keep upstream's `name`/`version`/`exports`/`type`, point declaration metadata at `lib/types`, publish `.d.ts` and `.d.ts.map` declaration outputs, and list its cordis deps in `peerDependencies` (matching the upstream manifest). Transitive upstream deps must themselves be vendored or already present — vendoring one package often means vendoring its dependency tree (e.g. `@cordisjs/plugin-http` pulls `@cordisjs/fetch-file`).
|
||||
`package.json` invariants: `"private": true` (vendored packages are never published), rescope the `name` ([mapping](../rescope.md)) while keeping upstream's `version`/`exports`/`type`, point declaration metadata at `lib/types`, publish `.d.ts` and `.d.ts.map` declaration outputs, and list its cordis deps in `peerDependencies` (matching the upstream manifest). Transitive upstream deps must themselves be vendored or already present — vendoring one package often means vendoring its dependency tree (e.g. `@cordisjs/plugin-http` pulls `@cordisjs/fetch-file`).
|
||||
|
||||
Local relative imports/exports in vendored TypeScript source use explicit `.ts` specifiers after copying. This is a repo-local build difference from upstream: `rewriteRelativeImportExtensions` emits `.js` runtime imports while declarations keep explicit `.ts` specifiers that NodeNext/Node16 TypeScript consumers can resolve.
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
```
|
||||
vendor/<dir>/
|
||||
package.json # from upstream; set "private": true, keep name/exports/type
|
||||
package.json # from upstream; set "private": true, rescope the name, keep exports/type
|
||||
tsconfig.json # extends ../../tsconfig.base.json (see configuration below)
|
||||
src/ # the upstream src/ verbatim
|
||||
README.md LICENSE # if upstream ships them
|
||||
@@ -29,7 +29,7 @@ vendor/<dir>/
|
||||
}
|
||||
```
|
||||
|
||||
`package.json` 的不变式:`"private": true`(vendored 包永不发布);保留上游的 `name`/`version`/`exports`/`type`;声明元数据指向 `lib/types`;发布 `.d.ts` 与 `.d.ts.map` 声明输出;在 `peerDependencies` 中列出其 Cordis 依赖(与上游 manifest(元数据清单)一致)。传递性上游依赖本身也必须被 vendor 或已存在于仓库中——vendor 一个包往往意味着 vendor 其整条依赖树(如 `@cordisjs/plugin-http` 会拉入 `@cordisjs/fetch-file`)。
|
||||
`package.json` 的不变式:`"private": true`(vendored 包永不发布);改写 `name` 的 scope([映射](../rescope.md)),保留上游的 `version`/`exports`/`type`;声明元数据指向 `lib/types`;发布 `.d.ts` 与 `.d.ts.map` 声明输出;在 `peerDependencies` 中列出其 Cordis 依赖(与上游 manifest(元数据清单)一致)。传递性上游依赖本身也必须被 vendor 或已存在于仓库中——vendor 一个包往往意味着 vendor 其整条依赖树(如 `@cordisjs/plugin-http` 会拉入 `@cordisjs/fetch-file`)。
|
||||
|
||||
vendored TypeScript 源码中的本地相对导入/导出在复制后使用显式 `.ts` 后缀。这是仓库本地构建与上游的差异:`rewriteRelativeImportExtensions` 输出 `.js` 运行时导入,而声明文件保留显式 `.ts` 后缀,使 NodeNext/Node16 的 TypeScript 消费方能够解析。
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/cookbook/extension-cookbook.md
|
||||
extension-cookbook.md: 95ba269a5d62e14cfde487d5a3aaca5db493657e
|
||||
extension-cookbook.zh.md: e3fbe09f1ec09568e3b259aee361d33ba3e62140
|
||||
extension-cookbook.md: f292075dfdad5016d81521318b38594e3d7ee8b4
|
||||
extension-cookbook.zh.md: 0623c49d9d7075b3823c1fd340b36a5fba21f31e
|
||||
|
||||
@@ -13,7 +13,7 @@ A tool registers on `ctx.tools`. The annotated `defineTool` example (typed `exec
|
||||
This permission gate is one example of a hook plugin. It returns a typed decision from the `tools/pre-execute` gate to allow or deny a call; sandbox, permission, and plan-mode plugins can use this extension point. Hook plugins can intercept other extension points and are not inherently permission gates. A "native hook" is an ordinary Cordis plugin on an interception point; it needs no external protocol.
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type { PreToolDecision, ToolExecution } from '@deepseek-ai/dsh-tools'
|
||||
|
||||
declare function isAllowed(exec: ToolExecution): Promise<boolean>
|
||||
@@ -37,7 +37,7 @@ This waterfall is the reorderable policy layer. Use `ctx.tools.guard()` when an
|
||||
A UI plugin renders from the `session/event` feed (the assistant token stream as `assistant/chunk`, plus turn/step boundaries and tool activity), and drives input back in via `agent.followup()` / `agent.steer()`. A browser plugin contributing a business row to the built-in Web Client instead registers a `ConversationNodeDefinition` and keyed Chat renderer; follow the [Conversation Node guide](adding-a-conversation-node.md).
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import { createUserMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { SessionId } from '@deepseek-ai/dsh-session'
|
||||
|
||||
@@ -67,7 +67,7 @@ A *protocol driver* adapts a wire peer to `ctx.agents`; it may serve a UI or an
|
||||
[`packages/acp/acp`](../../packages/acp/acp) is the automation-only worked example: it exposes fresh text sessions over Agent Client Protocol JSON-RPC stdio, emits committed assistant text, and registers a one-shot machine permission answerer for agents it owns. Its [README](../../packages/acp/acp/README.md) defines the exact methods, event order, and lifecycle contract.
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export const name = 'my-protocol-bridge'
|
||||
export const inject = ['agents', 'sessions', 'sessionPersistence']
|
||||
|
||||
@@ -13,7 +13,7 @@ harness 扩展的参考模式。代码片段省略了 import 和辅助实现,
|
||||
这个权限门禁是钩子插件的一个示例。它从 `tools/pre-execute` 门禁返回一个类型化的决策,用于允许或拒绝一次调用;沙箱、权限和 plan-mode 插件都可以使用该扩展点。钩子插件也可以拦截其他扩展点,本身并不等同于权限门禁。「原生钩子」是在拦截点上运行的普通 Cordis 插件,不需要外部协议。
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type { PreToolDecision, ToolExecution } from '@deepseek-ai/dsh-tools'
|
||||
|
||||
declare function isAllowed(exec: ToolExecution): Promise<boolean>
|
||||
@@ -37,7 +37,7 @@ export function apply(ctx: Context) {
|
||||
UI 插件从 `session/event` 事件流渲染(助手 token 流以 `assistant/chunk` 形式到达,加上轮次/步骤边界与工具活动),并通过 `agent.followup()` / `agent.steer()` 将输入驱动回去。如果浏览器插件要向内建 Web Client 贡献业务行,则应注册 `ConversationNodeDefinition` 与 keyed Chat renderer;具体步骤见 [Conversation Node 指南](adding-a-conversation-node.md)。
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import { createUserMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { SessionId } from '@deepseek-ai/dsh-session'
|
||||
|
||||
@@ -67,7 +67,7 @@ export function apply(ctx: Context) {
|
||||
[`packages/acp/acp`](../../packages/acp/acp) 是仅面向自动化的完整示例:它通过 ACP(Agent Client Protocol)JSON-RPC stdio 提供全新文本会话,发出已提交的助手文本,并为其拥有的 agent 注册一次性机器权限应答器。其 [README](../../packages/acp/acp/README.md) 定义确切的方法、事件顺序和生命周期约定。
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export const name = 'my-protocol-bridge'
|
||||
export const inject = ['agents', 'sessions', 'sessionPersistence']
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/cordis-primer.md
|
||||
cordis-primer.md: c95909a4a1deab9407efedbb990ef13be6e43a16
|
||||
cordis-primer.zh.md: a18b8b37af19a610b71babbe5e67f96bb09e81b1
|
||||
cordis-primer.md: 93725949a9490f757edebcf3e8391db9e73321b1
|
||||
cordis-primer.zh.md: fd2a327b526b210986bc1574013fca2c0cec5dda
|
||||
|
||||
@@ -35,7 +35,7 @@ For single-decision events, short-circuiting is the design. A policy listener ca
|
||||
|
||||
## Loader Configuration
|
||||
|
||||
`@cordisjs/plugin-include` parses `!!js` into expression nodes, but the Loader interpolates only an entry's `config` before mounting the plugin. Entry metadata (`id`, `name`, `group`, `disabled`, `inject`, `intercept`, and `isolate`) remains literal; `disabled: !!js ...` is therefore a truthy object that always disables the entry. Use explicit config overlays when environment selection changes which plugins are mounted.
|
||||
`@deepseek-ai/cordis-plugin-include` parses `!!js` into expression nodes, but the Loader interpolates only an entry's `config` before mounting the plugin. Entry metadata (`id`, `name`, `group`, `disabled`, `inject`, `intercept`, and `isolate`) remains literal; `disabled: !!js ...` is therefore a truthy object that always disables the entry. Use explicit config overlays when environment selection changes which plugins are mounted.
|
||||
|
||||
## Practical Rules
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ Cordis 是 DeepSeek Harness SDK 底层以 vendor 方式引入的插件框架。
|
||||
|
||||
## Loader 配置
|
||||
|
||||
`@cordisjs/plugin-include` 将 `!!js` 解析为表达式节点,但 Loader 仅在挂载插件前对条目的 `config` 做插值。条目元数据(`id`、`name`、`group`、`disabled`、`inject`、`intercept` 和 `isolate`)保持字面值;因此 `disabled: !!js ...` 是一个 truthy 对象,会始终禁用该条目。需要根据环境选择挂载哪些插件时,请使用显式的配置覆盖层。
|
||||
`@deepseek-ai/cordis-plugin-include` 将 `!!js` 解析为表达式节点,但 Loader 仅在挂载插件前对条目的 `config` 做插值。条目元数据(`id`、`name`、`group`、`disabled`、`inject`、`intercept` 和 `isolate`)保持字面值;因此 `disabled: !!js ...` 是一个 truthy 对象,会始终禁用该条目。需要根据环境选择挂载哪些插件时,请使用显式的配置覆盖层。
|
||||
|
||||
## 实践规则
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/cordis-tutorial/01-first-plugin.md
|
||||
01-first-plugin.md: 260026329443f9a5b8860d11a6527dbd687eb44c
|
||||
01-first-plugin.zh.md: 69dedb898c7ea29f99233f07126cd413fa0ddbe2
|
||||
01-first-plugin.md: 448034b54107d5620673052ad388feecc22fe1e1
|
||||
01-first-plugin.zh.md: a1838be61f8099831d59afa266fe40ce6bd165bf
|
||||
|
||||
@@ -9,7 +9,7 @@ In the loader configuration used here, a Cordis plugin module named-exports an `
|
||||
In your `tmp/cordis-tutorial` directory (see [setup](index.md#setup)), create `hello.ts`:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export const name = 'hello'
|
||||
|
||||
@@ -55,7 +55,7 @@ There is no framework bootstrap code in your file: a plugin describes what it co
|
||||
A function is the most common form, but Cordis accepts three:
|
||||
|
||||
```ts
|
||||
import { Service, type Context } from 'cordis'
|
||||
import { Service, type Context } from '@deepseek-ai/cordis'
|
||||
|
||||
// 1. Function plugin (what you just wrote).
|
||||
export function apply(ctx: Context) {}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
在 `tmp/cordis-tutorial` 目录中(参见[环境设置](index.md#setup))创建 `hello.ts`:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export const name = 'hello'
|
||||
|
||||
@@ -55,7 +55,7 @@ hello from my first plugin
|
||||
函数是最常见的形式,但 Cordis 接受三种形式:
|
||||
|
||||
```ts
|
||||
import { Service, type Context } from 'cordis'
|
||||
import { Service, type Context } from '@deepseek-ai/cordis'
|
||||
|
||||
// 1. Function plugin (what you just wrote).
|
||||
export function apply(ctx: Context) {}
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/cordis-tutorial/02-lifecycle-and-effects.md
|
||||
02-lifecycle-and-effects.md: 7b195b63a1e8730f27b9dd9af8af6a68a588cee9
|
||||
02-lifecycle-and-effects.zh.md: 4a3f83dedd5c95c7fcb5c1aebbbb8cb2e849b9cf
|
||||
02-lifecycle-and-effects.md: 3e88c6f1e1fb1bc825fb74434520993c855010c3
|
||||
02-lifecycle-and-effects.zh.md: 3cceecfb8334f2ed2ec9942fa876a0e51e1b315f
|
||||
|
||||
@@ -11,7 +11,7 @@ For a resource Cordis does not already manage — a timer, a connection, a watch
|
||||
Create `lifecycle.ts` in `tmp/cordis-tutorial`:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export const name = 'lifecycle-demo'
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ Cordis 插件可能因修改配置、热重载、显式资源释放或所需服
|
||||
创建 `lifecycle.ts`,将它放在 `tmp/cordis-tutorial` 中:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export const name = 'lifecycle-demo'
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/cordis-tutorial/03-services.md
|
||||
03-services.md: 82b08b7b8a2ec8a6b340dd1fdc7fa3de98cedff9
|
||||
03-services.zh.md: ba4152454eb79a21b183b867c0ba2ef32cd43923
|
||||
03-services.md: 3f2273ba4061912640e409d7b4deb4cd1b45684f
|
||||
03-services.zh.md: 657271aba6c0b2e824c79fa822e29c8a6dcf4275
|
||||
|
||||
@@ -9,9 +9,9 @@ A **service** is a named capability one plugin provides and other plugins consum
|
||||
Create `greeter.ts` in `tmp/cordis-tutorial`:
|
||||
|
||||
```ts
|
||||
import { Service, type Context } from 'cordis'
|
||||
import { Service, type Context } from '@deepseek-ai/cordis'
|
||||
|
||||
declare module 'cordis' {
|
||||
declare module '@deepseek-ai/cordis' {
|
||||
interface Context {
|
||||
greeter: GreeterService
|
||||
}
|
||||
@@ -37,7 +37,7 @@ export function apply(ctx: Context) {
|
||||
Two pieces work together:
|
||||
|
||||
- **Runtime**: `super(ctx, 'greeter')` registers the instance under the name `greeter`. From then on, any plugin can reach it as `ctx.greeter`. The registration is an effect — unloading the provider removes the service.
|
||||
- **Compile time**: the `declare module 'cordis'` block is TypeScript declaration merging. It adds `greeter` to the `Context` interface so `ctx.greeter` typechecks everywhere. It generates no code; without it the service still works at runtime, but consumers lose type safety.
|
||||
- **Compile time**: the `declare module '@deepseek-ai/cordis'` block is TypeScript declaration merging. It adds `greeter` to the `Context` interface so `ctx.greeter` typechecks everywhere. It generates no code; without it the service still works at runtime, but consumers lose type safety.
|
||||
|
||||
A `Service` subclass is itself a plugin (the class form from chapter 1), so `ctx.plugin(GreeterService)` mounts it like any other.
|
||||
|
||||
@@ -46,7 +46,7 @@ A `Service` subclass is itself a plugin (the class form from chapter 1), so `ctx
|
||||
Create `consumer.ts`:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export const name = 'consumer'
|
||||
export const inject = ['greeter']
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
创建 `greeter.ts`,将它放在 `tmp/cordis-tutorial` 中:
|
||||
|
||||
```ts
|
||||
import { Service, type Context } from 'cordis'
|
||||
import { Service, type Context } from '@deepseek-ai/cordis'
|
||||
|
||||
declare module 'cordis' {
|
||||
declare module '@deepseek-ai/cordis' {
|
||||
interface Context {
|
||||
greeter: GreeterService
|
||||
}
|
||||
@@ -37,7 +37,7 @@ export function apply(ctx: Context) {
|
||||
两部分协同工作:
|
||||
|
||||
- **运行时**:`super(ctx, 'greeter')` 以名称 `greeter` 注册该实例。此后,任何插件都可以通过 `ctx.greeter` 访问它。注册属于 effect,卸载提供方时会移除该服务。
|
||||
- **编译时**:`declare module 'cordis'` 块使用 TypeScript 声明合并,把 `greeter` 加入 `Context` 接口,使 `ctx.greeter` 在各处都能通过类型检查。它不会生成代码;没有该声明时,服务在运行时仍能工作,但消费方会失去类型安全。
|
||||
- **编译时**:`declare module '@deepseek-ai/cordis'` 块使用 TypeScript 声明合并,把 `greeter` 加入 `Context` 接口,使 `ctx.greeter` 在各处都能通过类型检查。它不会生成代码;没有该声明时,服务在运行时仍能工作,但消费方会失去类型安全。
|
||||
|
||||
`Service` 子类本身就是插件(第 1 章介绍的类形态),因此 `ctx.plugin(GreeterService)` 会像挂载其他插件一样挂载它。
|
||||
|
||||
@@ -46,7 +46,7 @@ export function apply(ctx: Context) {
|
||||
创建 `consumer.ts`:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export const name = 'consumer'
|
||||
export const inject = ['greeter']
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/cordis-tutorial/04-events.md
|
||||
04-events.md: e77641dffcb82fcb50a24ca2e3e764d152218094
|
||||
04-events.zh.md: 00cce854e9a54fddb594ffa8e306f60a725ac012
|
||||
04-events.md: 0302adf7c81b802b406f5f6737539ccf3eb970f9
|
||||
04-events.zh.md: ae41e757c06a46ef70c250c94a124546769e0112
|
||||
|
||||
@@ -9,9 +9,9 @@ Services support direct calls; **events** let a plugin announce something withou
|
||||
Create `stats.ts` in `tmp/cordis-tutorial` — a service that counts things and announces each change:
|
||||
|
||||
```ts
|
||||
import { Service, type Context } from 'cordis'
|
||||
import { Service, type Context } from '@deepseek-ai/cordis'
|
||||
|
||||
declare module 'cordis' {
|
||||
declare module '@deepseek-ai/cordis' {
|
||||
interface Context {
|
||||
stats: StatsService
|
||||
}
|
||||
@@ -46,7 +46,7 @@ The `interface Events` merge is the event-system twin of the `interface Context`
|
||||
Create `reporter.ts`:
|
||||
|
||||
```ts ignore-check
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type {} from './stats.ts'
|
||||
|
||||
export const name = 'reporter'
|
||||
@@ -96,9 +96,9 @@ Every harness event documents its mode in the generated reference on its owning
|
||||
Waterfall is the mode that powers interception. Each listener receives the arguments plus a `next()` continuation; it can transform what `next()` returns, or return without calling `next()` and short-circuit the rest of the chain — what the Cordis docs call the veto. Create `waterfall-demo.ts`:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
declare module 'cordis' {
|
||||
declare module '@deepseek-ai/cordis' {
|
||||
interface Events {
|
||||
'demo/transform'(input: string, next: () => Promise<string>): Promise<string>
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
创建 `stats.ts`,将它放在 `tmp/cordis-tutorial` 中。它是一项负责计数并在每次变化时发出通知的服务:
|
||||
|
||||
```ts
|
||||
import { Service, type Context } from 'cordis'
|
||||
import { Service, type Context } from '@deepseek-ai/cordis'
|
||||
|
||||
declare module 'cordis' {
|
||||
declare module '@deepseek-ai/cordis' {
|
||||
interface Context {
|
||||
stats: StatsService
|
||||
}
|
||||
@@ -46,7 +46,7 @@ export function apply(ctx: Context) {
|
||||
创建 `reporter.ts`:
|
||||
|
||||
```ts ignore-check
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type {} from './stats.ts'
|
||||
|
||||
export const name = 'reporter'
|
||||
@@ -96,9 +96,9 @@ export function apply(ctx: Context) {
|
||||
waterfall 是实现拦截的模式。每个监听器都会收到参数和一个 `next()` continuation;它可以转换 `next()` 的返回值,也可以不调用 `next()` 就直接返回,从而短路链条的其余部分。Cordis 文档把后一种行为称为否决。创建 `waterfall-demo.ts`:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
declare module 'cordis' {
|
||||
declare module '@deepseek-ai/cordis' {
|
||||
interface Events {
|
||||
'demo/transform'(input: string, next: () => Promise<string>): Promise<string>
|
||||
}
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/cordis-tutorial/05-config.md
|
||||
05-config.md: 834bb140cc1ff976acc8f21c8f54a7fb02636eac
|
||||
05-config.zh.md: f5cc6ac1ca4fa02eba6a1b015b9f6ae3b1a925fc
|
||||
05-config.md: ad73a732242e4015b2976e6fb193ff464e148dfa
|
||||
05-config.zh.md: edb3c4113288dfa80e899fb2e5eb21a67d929ca6
|
||||
|
||||
@@ -9,8 +9,8 @@ Each `cordis.yml` entry can carry a `config` block, and the plugin declares a sc
|
||||
Create `config-demo.ts` in `tmp/cordis-tutorial`:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import Schema from 'schemastery'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import Schema from '@deepseek-ai/schemastery'
|
||||
|
||||
export const name = 'config-demo'
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
创建 `config-demo.ts`,并将其放在 `tmp/cordis-tutorial` 中:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import Schema from 'schemastery'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import Schema from '@deepseek-ai/schemastery'
|
||||
|
||||
export const name = 'config-demo'
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/cordis-tutorial/06-composition-and-hmr.md
|
||||
06-composition-and-hmr.md: a169d7a164be63c939e352e4e5b0bf9bce43da29
|
||||
06-composition-and-hmr.zh.md: 07ae46555c390d625a4397933e2ec5ac059bd270
|
||||
06-composition-and-hmr.md: 490e3de3a98dd823190deefd47e1b6f2f8ba71b5
|
||||
06-composition-and-hmr.zh.md: 4aefb5ecd036929590ab373a2883d90779997b4b
|
||||
|
||||
@@ -22,24 +22,24 @@ Groups nest a sub-list of entries that load and unload as one unit, and `isolate
|
||||
|
||||
## Hot module replacement
|
||||
|
||||
Because unloading releases effects ([chapter 2](02-lifecycle-and-effects.md)) and loading follows dependencies ([chapter 3](03-services.md)), HMR can replace a running plugin by unloading and loading it. The `@cordisjs/plugin-hmr` plugin watches your files and does exactly that on save.
|
||||
Because unloading releases effects ([chapter 2](02-lifecycle-and-effects.md)) and loading follows dependencies ([chapter 3](03-services.md)), HMR can replace a running plugin by unloading and loading it. The `@deepseek-ai/cordis-plugin-hmr` plugin watches your files and does exactly that on save.
|
||||
|
||||
In `tmp/cordis-tutorial`, write `cordis.yml`:
|
||||
|
||||
```yaml
|
||||
- id: logger
|
||||
name: '@cordisjs/plugin-logger-console'
|
||||
name: '@deepseek-ai/cordis-plugin-logger-console'
|
||||
- id: timer
|
||||
name: '@cordisjs/plugin-timer'
|
||||
name: '@deepseek-ai/cordis-plugin-timer'
|
||||
- id: hmr
|
||||
name: '@cordisjs/plugin-hmr'
|
||||
name: '@deepseek-ai/cordis-plugin-hmr'
|
||||
config:
|
||||
root: ['.']
|
||||
- id: hello
|
||||
name: './hello.ts'
|
||||
```
|
||||
|
||||
Two support plugins joined the list: HMR logs through the Cordis logger service, so without a console exporter you would not see its messages, and it `inject`s the `timer` service for debouncing — without `@cordisjs/plugin-timer` it sits in PENDING forever, silently. That silence is the subject of the next section.
|
||||
Two support plugins joined the list: HMR logs through the Cordis logger service, so without a console exporter you would not see its messages, and it `inject`s the `timer` service for debouncing — without `@deepseek-ai/cordis-plugin-timer` it sits in PENDING forever, silently. That silence is the subject of the next section.
|
||||
|
||||
HMR reads Node's loader internals through the Loader's native helper. Run Cordis under tsx:
|
||||
|
||||
@@ -65,7 +65,7 @@ The flip side of dependency-driven loading: a plugin whose `inject` names a serv
|
||||
You can see the states directly. Every context can enumerate the plugin registry; create `diagnose.ts`:
|
||||
|
||||
```ts
|
||||
import { FiberState, type Context } from 'cordis'
|
||||
import { FiberState, type Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export const name = 'diagnose'
|
||||
|
||||
@@ -85,7 +85,7 @@ export function apply(ctx: Context) {
|
||||
And a plugin with an unsatisfiable dependency, `needs-timer.ts`:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export const name = 'needs-timer'
|
||||
export const inject = ['timer']
|
||||
@@ -106,7 +106,7 @@ Run it (plain `node --import tsx ../../vendor/cordis/bin.js`; stop with Ctrl-C):
|
||||
needs-timer is PENDING — a required service is missing
|
||||
```
|
||||
|
||||
`inject: ['timer']` has no provider. Add `- name: '@cordisjs/plugin-timer'` to the list and the plugin loads. When a plugin does nothing and reports nothing, inspect its fiber state. Iterating without the PENDING filter also shows the loader's own plugins (Loader, Include) as ACTIVE fibers because plugins mount the config file itself.
|
||||
`inject: ['timer']` has no provider. Add `- name: '@deepseek-ai/cordis-plugin-timer'` to the list and the plugin loads. When a plugin does nothing and reports nothing, inspect its fiber state. Iterating without the PENDING filter also shows the loader's own plugins (Loader, Include) as ACTIVE fibers because plugins mount the config file itself.
|
||||
|
||||
Next: [Into the harness](07-into-the-harness.md) — the same patterns against real harness services.
|
||||
|
||||
|
||||
@@ -22,24 +22,24 @@ Cordis 配置项除了 `name` 和 `config`,还接受其他元数据:
|
||||
|
||||
## 热模块替换
|
||||
|
||||
卸载会释放 effect([第 2 章](02-lifecycle-and-effects.md)),加载则遵循依赖关系([第 3 章](03-services.md)),因此 HMR 可以先卸载、再加载,以替换正在运行的插件。`@cordisjs/plugin-hmr` 插件会监视文件,并在保存时执行这一过程。
|
||||
卸载会释放 effect([第 2 章](02-lifecycle-and-effects.md)),加载则遵循依赖关系([第 3 章](03-services.md)),因此 HMR 可以先卸载、再加载,以替换正在运行的插件。`@deepseek-ai/cordis-plugin-hmr` 插件会监视文件,并在保存时执行这一过程。
|
||||
|
||||
在 `tmp/cordis-tutorial` 中编写 `cordis.yml`:
|
||||
|
||||
```yaml
|
||||
- id: logger
|
||||
name: '@cordisjs/plugin-logger-console'
|
||||
name: '@deepseek-ai/cordis-plugin-logger-console'
|
||||
- id: timer
|
||||
name: '@cordisjs/plugin-timer'
|
||||
name: '@deepseek-ai/cordis-plugin-timer'
|
||||
- id: hmr
|
||||
name: '@cordisjs/plugin-hmr'
|
||||
name: '@deepseek-ai/cordis-plugin-hmr'
|
||||
config:
|
||||
root: ['.']
|
||||
- id: hello
|
||||
name: './hello.ts'
|
||||
```
|
||||
|
||||
列表中增加了两个辅助插件:HMR 通过 Cordis logger 服务记录日志,因此没有控制台导出器时看不到其消息;它还会 `inject` `timer` 服务来实现去抖,如果没有 `@cordisjs/plugin-timer`,它就会永远停在 PENDING,而且不发出任何提示。下一节就讨论这种静默状态。
|
||||
列表中增加了两个辅助插件:HMR 通过 Cordis logger 服务记录日志,因此没有控制台导出器时看不到其消息;它还会 `inject` `timer` 服务来实现去抖,如果没有 `@deepseek-ai/cordis-plugin-timer`,它就会永远停在 PENDING,而且不发出任何提示。下一节就讨论这种静默状态。
|
||||
|
||||
HMR 通过 Loader 的原生辅助工具读取 Node 的 loader 内部结构。请在 tsx 下运行 Cordis:
|
||||
|
||||
@@ -65,7 +65,7 @@ hello from my EDITED plugin
|
||||
你可以直接查看这些状态。每个上下文都能枚举插件注册表;创建 `diagnose.ts`:
|
||||
|
||||
```ts
|
||||
import { FiberState, type Context } from 'cordis'
|
||||
import { FiberState, type Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export const name = 'diagnose'
|
||||
|
||||
@@ -85,7 +85,7 @@ export function apply(ctx: Context) {
|
||||
再创建一个依赖无法满足的插件 `needs-timer.ts`:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export const name = 'needs-timer'
|
||||
export const inject = ['timer']
|
||||
@@ -106,7 +106,7 @@ export function apply(ctx: Context) {
|
||||
needs-timer is PENDING — a required service is missing
|
||||
```
|
||||
|
||||
`inject: ['timer']` 没有提供方。向列表添加 `- name: '@cordisjs/plugin-timer'` 后,插件就会加载。如果插件既不执行任何操作,也不报告任何内容,请检查其 fiber 状态。不加 PENDING 过滤条件进行迭代时,还会看到 loader 自身的插件(Loader、Include)处于 ACTIVE,因为配置文件本身也是通过插件挂载的。
|
||||
`inject: ['timer']` 没有提供方。向列表添加 `- name: '@deepseek-ai/cordis-plugin-timer'` 后,插件就会加载。如果插件既不执行任何操作,也不报告任何内容,请检查其 fiber 状态。不加 PENDING 过滤条件进行迭代时,还会看到 loader 自身的插件(Loader、Include)处于 ACTIVE,因为配置文件本身也是通过插件挂载的。
|
||||
|
||||
下一章:[进入 harness](07-into-the-harness.md):把相同模式用于真实的 harness 服务。
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/cordis-tutorial/07-into-the-harness.md
|
||||
07-into-the-harness.md: 69133786f58541b015aed080f4ac8fb2a7e488c0
|
||||
07-into-the-harness.zh.md: bc9c61da984e3eb691eb6bfbe59ae556823e82de
|
||||
07-into-the-harness.md: 41a30f032ac02d8e9e3b17ac8d9cd13e9973e36a
|
||||
07-into-the-harness.zh.md: 159cede00d453796f6a2cacae184229d71e17f87
|
||||
|
||||
@@ -9,7 +9,7 @@ This chapter registers a model-callable tool with the harness's `tools` service,
|
||||
Create `greet-tool.ts` in `tmp/cordis-tutorial`:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import { defineTool } from '@deepseek-ai/dsh-tools'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
|
||||
@@ -53,7 +53,7 @@ Every pattern here is from the earlier chapters: `inject: ['tools']` ([chapter 3
|
||||
Create `tool-logger.ts` — a separate plugin that watches every tool call in the app through the harness's `tools/result` event:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type {} from '@deepseek-ai/dsh-tools'
|
||||
|
||||
export const name = 'tool-logger'
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
创建 `greet-tool.ts`,将它放在 `tmp/cordis-tutorial` 中:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import { defineTool } from '@deepseek-ai/dsh-tools'
|
||||
import { CallId } from '@deepseek-ai/dsh-llm'
|
||||
|
||||
@@ -53,7 +53,7 @@ export function apply(ctx: Context) {
|
||||
创建 `tool-logger.ts`。这是一个独立插件,通过 harness 的 `tools/result` 事件观察应用中的每次工具调用:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import type {} from '@deepseek-ai/dsh-tools'
|
||||
|
||||
export const name = 'tool-logger'
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/cordis-tutorial/index.md
|
||||
index.md: 307c12854b3075cfd4dd5ea8a19806c58b4e998d
|
||||
index.zh.md: a0107b7d15272e6ef8d526b9c0e03a99275644d6
|
||||
index.md: cf61fec07acd2022591cd858ba4146b6a4ae1a3d
|
||||
index.zh.md: 775bf2fce2138b9edd91f5ee46b93b8c4eb4559e
|
||||
|
||||
@@ -50,8 +50,8 @@ That one-file launcher (see [vendor/cordis/bin.js](../../vendor/cordis/bin.js))
|
||||
The examples use three TypeScript features beyond ordinary modern JavaScript:
|
||||
|
||||
- **Type annotations** describe values without changing runtime behavior: `ctx: Context` says that `ctx` has the Cordis context API, `who: string` accepts text, and `string[]` means an array of strings.
|
||||
- **`import type { Context } from 'cordis'`** imports only type information. It vanishes at runtime, so a plugin file that needs `Context` solely for annotations adds no runtime dependency.
|
||||
- **Declaration merging** (`declare module 'cordis' { ... }`) adds your entries to interfaces that Cordis already declares — for example the type of a new `ctx.greeter` property or event name. It generates no runtime wiring; the plugin separately provides the service or emits the event. Chapter 3 shows the pattern in full.
|
||||
- **`import type { Context } from '@deepseek-ai/cordis'`** imports only type information. It vanishes at runtime, so a plugin file that needs `Context` solely for annotations adds no runtime dependency.
|
||||
- **Declaration merging** (`declare module '@deepseek-ai/cordis' { ... }`) adds your entries to interfaces that Cordis already declares — for example the type of a new `ctx.greeter` property or event name. It generates no runtime wiring; the plugin separately provides the service or emits the event. Chapter 3 shows the pattern in full.
|
||||
|
||||
Chapter 5 also uses an `interface` to describe a configuration object's fields and a generic type such as `Schema<Config>` to say which object fields a schema validates. You can copy those declarations as shown; the surrounding text explains what each one connects.
|
||||
|
||||
|
||||
@@ -50,8 +50,8 @@ node --import tsx ../../vendor/cordis/bin.js
|
||||
这些示例使用了普通现代 JavaScript 之外的三项 TypeScript 功能:
|
||||
|
||||
- **类型注解** 描述值,但不会改变运行时行为:`ctx: Context` 表示 `ctx` 具备 Cordis 上下文 API,`who: string` 接受文本,而 `string[]` 表示字符串数组。
|
||||
- **`import type { Context } from 'cordis'`** 只导入类型信息。它在运行时会消失,因此仅为类型注解使用 `Context` 的插件文件不会增加运行时依赖。
|
||||
- **声明合并**(`declare module 'cordis' { ... }`)会为 Cordis 已经声明的接口添加你的条目,例如新 `ctx.greeter` 属性的类型或事件名称。它不会生成任何运行时接线;插件必须另行提供服务或发出事件。第 3 章会完整展示该模式。
|
||||
- **`import type { Context } from '@deepseek-ai/cordis'`** 只导入类型信息。它在运行时会消失,因此仅为类型注解使用 `Context` 的插件文件不会增加运行时依赖。
|
||||
- **声明合并**(`declare module '@deepseek-ai/cordis' { ... }`)会为 Cordis 已经声明的接口添加你的条目,例如新 `ctx.greeter` 属性的类型或事件名称。它不会生成任何运行时接线;插件必须另行提供服务或发出事件。第 3 章会完整展示该模式。
|
||||
|
||||
第 5 章还会使用 `interface` 描述配置对象的字段,并使用 `Schema<Config>` 这类泛型表示 schema 校验哪些对象字段。你可以直接照写这些声明;周围的正文会解释每项声明连接了什么。
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/develop/basic/config.md
|
||||
config.md: 02998c32415b5ba7acf82700034cabc1f7314f33
|
||||
config.zh.md: 161af5d6703b4cc77d63443a5a80846593d1c7fd
|
||||
config.md: 21ba39fd7de1795e9139aff3e2b11743eedd4833
|
||||
config.zh.md: a882c4d59b0ac8e8ec27a5b32da5376b534a7f62
|
||||
|
||||
@@ -9,8 +9,8 @@ Accept configuration supplied through `cordis.yml`.
|
||||
Export a `Config` type and a same-named Schemastery schema. Put defaults directly on the schema fields:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import Schema from 'schemastery'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import Schema from '@deepseek-ai/schemastery'
|
||||
|
||||
export const name = 'my-plugin'
|
||||
|
||||
@@ -49,8 +49,8 @@ When loading the plugin, Cordis uses the exported schema to validate configurati
|
||||
Use Schemastery to express stricter validation:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import Schema from 'schemastery'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import Schema from '@deepseek-ai/schemastery'
|
||||
|
||||
export const name = 'validated-plugin'
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
在插件中导出一个 `Config` 类型和同名的 Schemastery schema;默认值直接写在 schema 中:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import Schema from 'schemastery'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import Schema from '@deepseek-ai/schemastery'
|
||||
|
||||
export const name = 'my-plugin'
|
||||
|
||||
@@ -49,8 +49,8 @@ export function apply(ctx: Context, config: Config) {
|
||||
对于需要严格校验的场景,使用 Schemastery 定义 schema:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import Schema from 'schemastery'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import Schema from '@deepseek-ai/schemastery'
|
||||
|
||||
export const name = 'validated-plugin'
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/develop/basic/index.md
|
||||
index.md: 7fe66bb19ddb978a4b5a96768b62151b97bca0ec
|
||||
index.zh.md: 59f4e5b58b6cf1fbc15de8fafb5f4b0db2e220d6
|
||||
index.md: fe525018011809abe4dd18ac5739c54cd330c8f6
|
||||
index.zh.md: 0dbb7275be3c7147395041fb18d49a7f6ea69cdd
|
||||
|
||||
@@ -17,7 +17,7 @@ mkdir -p scratch-plugin/src
|
||||
In Harness, a plugin is a TypeScript module that exports an `apply` function. The framework calls `apply` when loading the plugin and passes a `ctx` context object through which the plugin registers capabilities:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export const name = 'my-plugin'
|
||||
|
||||
@@ -33,7 +33,7 @@ That is the complete configuration.
|
||||
Create `scratch-plugin/src/my-plugin.ts`:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export const name = 'hello-plugin'
|
||||
|
||||
@@ -68,7 +68,7 @@ Anything registered through `ctx`—event listeners, tools, or timers—is clean
|
||||
For a resource that needs explicit cleanup, such as a network connection, use `ctx.effect()` to provide its disposer:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export function apply(ctx: Context) {
|
||||
ctx.effect(() => {
|
||||
@@ -87,7 +87,7 @@ export function apply(ctx: Context) {
|
||||
If the plugin consumes another service such as `tools` or `llm`, declare it in `inject`:
|
||||
|
||||
```ts ignore-check
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export const name = 'my-tool-plugin'
|
||||
export const inject = ['tools']
|
||||
@@ -107,7 +107,7 @@ In addition to a function module, a plugin can use object or class form.
|
||||
### Object form
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export default {
|
||||
name: 'my-plugin',
|
||||
@@ -121,7 +121,7 @@ export default {
|
||||
### Class form
|
||||
|
||||
```ts
|
||||
import { Service, type Context } from 'cordis'
|
||||
import { Service, type Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export default class MyService extends Service {
|
||||
static inject = ['tools']
|
||||
|
||||
@@ -17,7 +17,7 @@ mkdir -p scratch-plugin/src
|
||||
在 Harness 中,插件是一个导出 `apply` 函数的 TypeScript 模块。框架在加载时调用 `apply`,传入一个 `ctx`(上下文对象),你通过 `ctx` 注册能力:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export const name = 'my-plugin'
|
||||
|
||||
@@ -33,7 +33,7 @@ export function apply(ctx: Context) {
|
||||
创建 `scratch-plugin/src/my-plugin.ts`:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export const name = 'hello-plugin'
|
||||
|
||||
@@ -68,7 +68,7 @@ pnpm run dsh web --patch ./scratch-plugin/cordis.yml
|
||||
如果你有需要手动清理的资源(比如一个网络连接),用 `ctx.effect()` 告诉框架怎么清理:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export function apply(ctx: Context) {
|
||||
ctx.effect(() => {
|
||||
@@ -87,7 +87,7 @@ export function apply(ctx: Context) {
|
||||
如果你的插件需要使用其他服务(如 `tools`、`llm`),需要声明 `inject`:
|
||||
|
||||
```ts ignore-check
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export const name = 'my-tool-plugin'
|
||||
export const inject = ['tools']
|
||||
@@ -107,7 +107,7 @@ export function apply(ctx: Context) {
|
||||
### 对象形式
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export default {
|
||||
name: 'my-plugin',
|
||||
@@ -121,7 +121,7 @@ export default {
|
||||
### 类形式
|
||||
|
||||
```ts
|
||||
import { Service, type Context } from 'cordis'
|
||||
import { Service, type Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export default class MyService extends Service {
|
||||
static inject = ['tools']
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/develop/basic/tool.md
|
||||
tool.md: ba2f3b1302ba31735d67be264f498a0395394d06
|
||||
tool.zh.md: 676f8fc996d752a05d94b55d4522e61e3b3e2161
|
||||
tool.md: f110bd2c10caf21ea8c87bc32fd43b01b209a9d1
|
||||
tool.zh.md: a237b6015de0ea6141a94c56aaa6f68c73fa3feb
|
||||
|
||||
@@ -9,7 +9,7 @@ This tutorial adds a `greet` tool to the Web UI. Complete [Your first plugin](./
|
||||
Replace `scratch-plugin/src/my-plugin.ts` with:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import { defineTool } from '@deepseek-ai/dsh-tools'
|
||||
|
||||
export const name = 'greet-tool'
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
将 `scratch-plugin/src/my-plugin.ts` 替换为:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import { defineTool } from '@deepseek-ai/dsh-tools'
|
||||
|
||||
export const name = 'greet-tool'
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/develop/framework/events.md
|
||||
events.md: 8a8c076d9c7b40d73182db074c4f494fded8c6dd
|
||||
events.zh.md: 9649d89d575a1b05fa524bd460043da71dc9ae43
|
||||
events.md: 4b5f9ee215186398ee5aee7a438f792f9b5a3639
|
||||
events.zh.md: b48c8020803239d3c9636f81052d3b70afa315f2
|
||||
|
||||
@@ -85,9 +85,9 @@ A waterfall listener **must call `next()`**. Omitting it short-circuits the pipe
|
||||
Harness uses TypeScript declaration merging for type-safe events:
|
||||
|
||||
```ts
|
||||
import 'cordis'
|
||||
import '@deepseek-ai/cordis'
|
||||
|
||||
declare module 'cordis' {
|
||||
declare module '@deepseek-ai/cordis' {
|
||||
interface Events {
|
||||
'my-plugin/ready': (payload: { id: string }) => void
|
||||
'my-plugin/check': (input: string) => boolean | undefined
|
||||
@@ -121,7 +121,7 @@ export function apply(ctx: Context) {
|
||||
This plugin logs tool calls and results:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import '@deepseek-ai/dsh-tools'
|
||||
|
||||
export const name = 'tool-logger'
|
||||
|
||||
@@ -85,9 +85,9 @@ waterfall 监听器**必须调用 `next()`**。不调用 `next` 会短路整个
|
||||
Harness 使用 TypeScript 声明合并来为事件提供类型安全:
|
||||
|
||||
```ts
|
||||
import 'cordis'
|
||||
import '@deepseek-ai/cordis'
|
||||
|
||||
declare module 'cordis' {
|
||||
declare module '@deepseek-ai/cordis' {
|
||||
interface Events {
|
||||
'my-plugin/ready': (payload: { id: string }) => void
|
||||
'my-plugin/check': (input: string) => boolean | undefined
|
||||
@@ -121,7 +121,7 @@ export function apply(ctx: Context) {
|
||||
这个插件记录工具调用和工具结果:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import '@deepseek-ai/dsh-tools'
|
||||
|
||||
export const name = 'tool-logger'
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/develop/framework/index.md
|
||||
index.md: 79e925b54509da41535735527e283850384257ec
|
||||
index.zh.md: 962677dc468c9cc233a51d50758247e028d9c3ed
|
||||
index.md: 85701ce281d92da0c805b39291179df73eb65f51
|
||||
index.zh.md: 871aa55ef81a7dcbfe3cbde5986244220ee32f98
|
||||
|
||||
@@ -80,7 +80,7 @@ export function apply(ctx: Context) {
|
||||
To stop a plugin instance early:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
declare const ctx: Context
|
||||
declare function myPlugin(ctx: Context): void
|
||||
@@ -98,7 +98,7 @@ await fiber.dispose()
|
||||
|
||||
## Hot replacement (HMR)
|
||||
|
||||
With `@cordisjs/plugin-hmr` loaded from `cordis.yml`, editing a plugin source file triggers:
|
||||
With `@deepseek-ai/cordis-plugin-hmr` loaded from `cordis.yml`, editing a plugin source file triggers:
|
||||
|
||||
1. Unload the old plugin and clean up its registrations.
|
||||
2. Load the new code.
|
||||
|
||||
@@ -80,7 +80,7 @@ export function apply(ctx: Context) {
|
||||
当你需要提前终止一个插件实例:
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
|
||||
declare const ctx: Context
|
||||
declare function myPlugin(ctx: Context): void
|
||||
@@ -98,7 +98,7 @@ await fiber.dispose()
|
||||
|
||||
## HMR(热模块替换)
|
||||
|
||||
通过 `cordis.yml` 加载 `@cordisjs/plugin-hmr` 后,修改插件源文件会触发:
|
||||
通过 `cordis.yml` 加载 `@deepseek-ai/cordis-plugin-hmr` 后,修改插件源文件会触发:
|
||||
|
||||
1. 卸载旧插件(清理所有注册)
|
||||
2. 重新加载新代码
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/develop/framework/service.md
|
||||
service.md: 040b1388cc431c30045e05f7d372ab5885bb3f9d
|
||||
service.zh.md: 0786b684c1688440a24cc729288835ad636f8ff7
|
||||
service.md: 3358f82ca5391741a7f531b7504de7335959ad03
|
||||
service.zh.md: 8fb4beeac43051c0f08483fe61e22c588127c360
|
||||
|
||||
@@ -36,7 +36,7 @@ When `apply` runs, every service declared by `inject` is ready. If a service is
|
||||
### Extend Service
|
||||
|
||||
```ts
|
||||
import { Service, type Context } from 'cordis'
|
||||
import { Service, type Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export default class MetricsService extends Service {
|
||||
static inject = ['llm'] // A service may depend on other services.
|
||||
@@ -67,9 +67,9 @@ export function apply(ctx: Context) {
|
||||
Use TypeScript declaration merging to type `ctx.metrics`:
|
||||
|
||||
```ts
|
||||
import { Service, type Context } from 'cordis'
|
||||
import { Service, type Context } from '@deepseek-ai/cordis'
|
||||
|
||||
declare module 'cordis' {
|
||||
declare module '@deepseek-ai/cordis' {
|
||||
interface Context {
|
||||
metrics: MetricsService
|
||||
}
|
||||
@@ -114,7 +114,7 @@ This prevents a plugin from calling a service that no longer exists.
|
||||
|
||||
```yaml
|
||||
- id: group-a
|
||||
name: '@cordisjs/plugin-group'
|
||||
name: '@deepseek-ai/cordis-plugin-group'
|
||||
group: true
|
||||
isolate:
|
||||
bash: true
|
||||
@@ -125,7 +125,7 @@ This prevents a plugin from calling a service that no longer exists.
|
||||
- name: './src/plugin-a.ts'
|
||||
|
||||
- id: group-b
|
||||
name: '@cordisjs/plugin-group'
|
||||
name: '@deepseek-ai/cordis-plugin-group'
|
||||
group: true
|
||||
isolate:
|
||||
bash: true
|
||||
|
||||
@@ -36,7 +36,7 @@ export function apply(ctx: Context) {
|
||||
### 使用 Service 基类
|
||||
|
||||
```ts
|
||||
import { Service, type Context } from 'cordis'
|
||||
import { Service, type Context } from '@deepseek-ai/cordis'
|
||||
|
||||
export default class MetricsService extends Service {
|
||||
static inject = ['llm'] // A service may depend on other services.
|
||||
@@ -67,9 +67,9 @@ export function apply(ctx: Context) {
|
||||
使用 TypeScript 声明合并让 `ctx.metrics` 有正确类型:
|
||||
|
||||
```ts
|
||||
import { Service, type Context } from 'cordis'
|
||||
import { Service, type Context } from '@deepseek-ai/cordis'
|
||||
|
||||
declare module 'cordis' {
|
||||
declare module '@deepseek-ai/cordis' {
|
||||
interface Context {
|
||||
metrics: MetricsService
|
||||
}
|
||||
@@ -114,7 +114,7 @@ export function apply(ctx: Context) {
|
||||
|
||||
```yaml
|
||||
- id: group-a
|
||||
name: '@cordisjs/plugin-group'
|
||||
name: '@deepseek-ai/cordis-plugin-group'
|
||||
group: true
|
||||
isolate:
|
||||
bash: true
|
||||
@@ -125,7 +125,7 @@ export function apply(ctx: Context) {
|
||||
- name: './src/plugin-a.ts'
|
||||
|
||||
- id: group-b
|
||||
name: '@cordisjs/plugin-group'
|
||||
name: '@deepseek-ai/cordis-plugin-group'
|
||||
group: true
|
||||
isolate:
|
||||
bash: true
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/develop/practice/index.md
|
||||
index.md: 1eb33e17ab6c5d0a2b37ff97d5948dfbcba497ca
|
||||
index.zh.md: 31afa80407f81f571615b5ed68a9370775f5188f
|
||||
index.md: 7ca9f0b1abe472dc90c6d4e56543e43b6d2ec727
|
||||
index.zh.md: 216b1cb01b355e411bf1949c59fd3720ae47139e
|
||||
|
||||
@@ -61,9 +61,9 @@ The [capability-seam reference](../../../capability-seams.md) owns the current b
|
||||
|
||||
```ts ignore-check
|
||||
// packages/my-cap/my-cap/src/index.ts
|
||||
import { Service, type Context } from 'cordis'
|
||||
import { Service, type Context } from '@deepseek-ai/cordis'
|
||||
|
||||
declare module 'cordis' {
|
||||
declare module '@deepseek-ai/cordis' {
|
||||
interface Context {
|
||||
myCap: MyCapService
|
||||
}
|
||||
@@ -91,7 +91,7 @@ export interface MyCapResult {
|
||||
|
||||
```ts ignore-check
|
||||
// packages/my-cap/my-cap-local/src/index.ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import { MyCapService, type MyCapRequest, type MyCapResult } from '@deepseek-ai/dsh-my-cap'
|
||||
|
||||
class MyCapLocal extends MyCapService {
|
||||
@@ -112,7 +112,7 @@ export function apply(ctx: Context) {
|
||||
|
||||
```ts ignore-check
|
||||
// packages/my-cap/tool-my-cap/src/index.ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import { defineTool } from '@deepseek-ai/dsh-tools'
|
||||
|
||||
export const name = 'tool-my-cap'
|
||||
|
||||
@@ -61,9 +61,9 @@
|
||||
|
||||
```ts ignore-check
|
||||
// packages/my-cap/my-cap/src/index.ts
|
||||
import { Service, type Context } from 'cordis'
|
||||
import { Service, type Context } from '@deepseek-ai/cordis'
|
||||
|
||||
declare module 'cordis' {
|
||||
declare module '@deepseek-ai/cordis' {
|
||||
interface Context {
|
||||
myCap: MyCapService
|
||||
}
|
||||
@@ -91,7 +91,7 @@ export interface MyCapResult {
|
||||
|
||||
```ts ignore-check
|
||||
// packages/my-cap/my-cap-local/src/index.ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import { MyCapService, type MyCapRequest, type MyCapResult } from '@deepseek-ai/dsh-my-cap'
|
||||
|
||||
class MyCapLocal extends MyCapService {
|
||||
@@ -112,7 +112,7 @@ export function apply(ctx: Context) {
|
||||
|
||||
```ts ignore-check
|
||||
// packages/my-cap/tool-my-cap/src/index.ts
|
||||
import type { Context } from 'cordis'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import { defineTool } from '@deepseek-ai/dsh-tools'
|
||||
|
||||
export const name = 'tool-my-cap'
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write docs/user/develop/practice/llm-adapter.md
|
||||
llm-adapter.md: 7445688530c1ba61e5c065f9f5e49db6498da5b1
|
||||
llm-adapter.zh.md: c726735ff2679584d1c061ef8acddc8981dadd26
|
||||
llm-adapter.md: aba4a6d0c8ee42e78ca5a804d9a0dd9b31c1e240
|
||||
llm-adapter.zh.md: dff9eef464599823d6cd99e83d668485109b2ec0
|
||||
|
||||
@@ -11,8 +11,8 @@ An LLM adapter extends `LlmAdapter` and implements `stream()`, translating Harne
|
||||
## Minimal implementation
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import Schema from 'schemastery'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import Schema from '@deepseek-ai/schemastery'
|
||||
import { LlmAdapter, type GenerateOptions, type StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
|
||||
class MyAdapter extends LlmAdapter {
|
||||
|
||||
@@ -11,8 +11,8 @@ LLM 适配器是一个继承 `LlmAdapter` 并实现 `stream()` 方法的类,
|
||||
## 最小实现
|
||||
|
||||
```ts
|
||||
import type { Context } from 'cordis'
|
||||
import Schema from 'schemastery'
|
||||
import type { Context } from '@deepseek-ai/cordis'
|
||||
import Schema from '@deepseek-ai/schemastery'
|
||||
import { LlmAdapter, type GenerateOptions, type StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
|
||||
class MyAdapter extends LlmAdapter {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Replay counterpart to advanced.cordis.yml; only the live model is replaced.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
name: '@deepseek-ai/cordis-plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Add Code Mode and Cordis tools to the base spawn/workflow stack, exercising
|
||||
# all four boundaries in one ACP snapshot.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
name: '@deepseek-ai/cordis-plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# swap. Include patches cannot target entries behind a nested include, so this file
|
||||
# applies both overlays directly to `cordis.yml`.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
name: '@deepseek-ai/cordis-plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# this overlay for snapshot recording and the sibling overlay for replay. A config
|
||||
# patch replaces the whole app config, so unchanged base fields are restated below.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
name: '@deepseek-ai/cordis-plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# seam, model-facing tool, and tripwire provider while replacing DeepSeek with
|
||||
# per-session replay.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
name: '@deepseek-ai/cordis-plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Snapshot-only human-interaction composition. The provider is a tripwire: the
|
||||
# runtime-owned child must be rejected by the seam before any UI wait begins.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
name: '@deepseek-ai/cordis-plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Keyless replay counterpart of code-mode-workspace-context.cordis.yml. It adds
|
||||
# Code Mode to the default filesystem suite and swaps in replay.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
name: '@deepseek-ai/cordis-plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Code Mode workspace-context snapshot recording overlay. The default filesystem
|
||||
# tools trigger nested instruction discovery after a read.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
name: '@deepseek-ai/cordis-plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# swap. Include patches cannot target entries behind a nested include, so this file
|
||||
# applies both overlays directly to `cordis.yml`.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
name: '@deepseek-ai/cordis-plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# replay overlay for `DSH_SNAPSHOT=replay`. A config patch replaces the whole app
|
||||
# config, so unchanged base fields are restated below.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
name: '@deepseek-ai/cordis-plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Add the self-referential Cordis tools without changing the base ACP tool
|
||||
# presentation mode.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
name: '@deepseek-ai/cordis-plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# `DSH_SNAPSHOT_OVERRIDE` from the harness. The one-shot patch applies at include
|
||||
# load time, and stdout remains reserved for ACP JSON-RPC.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
name: '@deepseek-ai/cordis-plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Keyless counterpart to depth-two.cordis.yml: apply the depth patch and replace
|
||||
# the live adapter with per-session replay.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
name: '@deepseek-ai/cordis-plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Depth-limit snapshot overlay: keep the default composition and allow two
|
||||
# generations of spawn children before runtime enforcement rejects another.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
name: '@deepseek-ai/cordis-plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# `deepseek-v4-pro`, but the recorded corpus was captured on flash, and a config
|
||||
# patch replaces the whole app config, so the base fields are restated verbatim.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
name: '@deepseek-ai/cordis-plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# the base cordis.yml, so this overlay adds only the local tool-result spill
|
||||
# storage those scenarios exercise.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
name: '@deepseek-ai/cordis-plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Keyless runner-classification composition: replay authored model turns and
|
||||
# replace the shipping provider with a deterministic process-launch stand-in.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
name: '@deepseek-ai/cordis-plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Live counterpart for the runner-classification snapshot overlay. It replaces
|
||||
# only the sandbox provider; authored scenarios are skipped in record mode.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
name: '@deepseek-ai/cordis-plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Keyless replay counterpart to pty.cordis.yml.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
name: '@deepseek-ai/cordis-plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user