@4fu/dsh-arbor-host 0.0.0-stage → 2026.1110.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +249 -2
- package/index.d.ts +383 -0
- package/index.js +898 -0
- package/package.json +60 -4
- package/send.d.ts +82 -0
- package/send.js +206 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 4fuu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,250 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dsh-arbor
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Run [arbor](https://github.com/4fuu/arbor) as the agent driver inside
|
|
4
|
+
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (DSH).
|
|
5
|
+
|
|
6
|
+
DSH gives a user a session, a transcript, approvals, plugins, skills and a model
|
|
7
|
+
picker. arbor gives an agent disposable context windows over one shared journal,
|
|
8
|
+
note store and workspace, instead of one long conversation. dsh-arbor connects
|
|
9
|
+
them: one arbor host runs inside the DSH process, each DSH session that selects
|
|
10
|
+
**Arbor** is served by that host, and the window's reply is written back to the
|
|
11
|
+
DSH session as an ordinary assistant message. A DSH session that does not select
|
|
12
|
+
Arbor keeps running on DSH's own embedded AgentLoop, in the same process.
|
|
13
|
+
|
|
14
|
+
> **Status.** This is a research-grade integration, not a DSH release. It is
|
|
15
|
+
> pinned to one DSH version and relies on DSH internals. Read
|
|
16
|
+
> [Versions and compatibility](docs/versions.md) before installing it on
|
|
17
|
+
> anything you care about. The packages are published under `@4fu` at
|
|
18
|
+
> `2026.1110.1`; the bundle and the font plugin install independently from npm.
|
|
19
|
+
|
|
20
|
+
## What an arbor session changes
|
|
21
|
+
|
|
22
|
+
Choosing **Arbor** in a DSH composer creates the session under the `arbor` agent
|
|
23
|
+
preset. That session then runs on arbor, and the following are true for it:
|
|
24
|
+
|
|
25
|
+
- **One window per turn, one chain per session.** A DSH session is one arbor
|
|
26
|
+
conversation: its first turn opens a window, a window that ends hands off to a
|
|
27
|
+
continuation, and the next turn drives the chain. The journal and note store
|
|
28
|
+
live under `$DSH_HOME/arbor/` and survive a restart.
|
|
29
|
+
- **arbor's workspace, not DSH's sandbox.** A window works in arbor's shared
|
|
30
|
+
folder (`$DSH_HOME/arbor/shared`). When the `ovnfs` binary is available a
|
|
31
|
+
window opens a copy-on-write checkout and merges back with `apply`; otherwise
|
|
32
|
+
arbor uses a plain directory. **There is no DSH sandbox for an arbor session:**
|
|
33
|
+
arbor's file tools and commands run on the host, and arbor's own secret scrub
|
|
34
|
+
is the only protection on what reaches the model.
|
|
35
|
+
- **arbor's projects.** The shared folder's project folders are the DSH project
|
|
36
|
+
list for arbor composers. A directory the user adds outside the shared folder
|
|
37
|
+
is recorded as an external pointer for that session and used in place.
|
|
38
|
+
- **arbor's prompt and tools.** DSH's official prompt sections are decided one by
|
|
39
|
+
one; the ones arbor replaces are absent, third-party sections go into a
|
|
40
|
+
`<dsh>` data block, and a trusted `<harness>` section says what remains true of
|
|
41
|
+
DSH. DSH tools are offered to the model as arbor external tools, and arbor's
|
|
42
|
+
own tools replace DSH tools with the same capability.
|
|
43
|
+
- **The Arbor page.** A permanent **Arbor** entry in DSH's left navigation opens
|
|
44
|
+
the whole-host overview and one tab per arbor session: windows, chains,
|
|
45
|
+
handoffs, projects, the board, notes, `/env`, schedules and arbor's search.
|
|
46
|
+
An arbor composer hides DSH's sandbox access-mode switch, because that switch
|
|
47
|
+
does not apply to it.
|
|
48
|
+
|
|
49
|
+
## What stays untouched in ordinary DSH sessions
|
|
50
|
+
|
|
51
|
+
A session that does not select Arbor is an ordinary DSH session. It keeps:
|
|
52
|
+
|
|
53
|
+
- DSH's embedded AgentLoop, session format, turn boundaries and streaming path;
|
|
54
|
+
- DSH's sandbox policy and its `ctx.fs` / `ctx.shell` file and command tools;
|
|
55
|
+
- DSH's project management, working directory and prompt assembly;
|
|
56
|
+
- its composer, sandbox access-mode switch, header and sidebar behavior.
|
|
57
|
+
|
|
58
|
+
It gets **no Arbor tab** and no arbor windows. The only shared surface is the
|
|
59
|
+
permanent Arbor entry in the left navigation, which simply does not list that
|
|
60
|
+
session. The routing registry replaces DSH's base `agent` row so it can send
|
|
61
|
+
`agentPreset: 'arbor'` sessions to arbor; every other session goes to the
|
|
62
|
+
official loop. Approvals still use DSH's approval path for both kinds of
|
|
63
|
+
session, and arbor's windows are registered so DSH's sidebar leaves them out.
|
|
64
|
+
|
|
65
|
+
## Screenshots
|
|
66
|
+
|
|
67
|
+
| Overview, light | Overview, dark |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
|  |  |
|
|
70
|
+
| **Session tab, light** | **Session tab, dark** |
|
|
71
|
+
|  |  |
|
|
72
|
+
|
|
73
|
+
These are the web profile at 1440 px wide with the optional
|
|
74
|
+
[font plugin](https://github.com/4fuu/dsh-arbor-fonts) installed: ChillDIN
|
|
75
|
+
Gothic for labels and ChillDIN for numerals, while body text and code keep
|
|
76
|
+
DSH's fonts. The same overview without the plugin is
|
|
77
|
+
[here](docs/screenshots/overview-without-fonts.png).
|
|
78
|
+
|
|
79
|
+
## Install
|
|
80
|
+
|
|
81
|
+
Requirements: Node.js 24.9 or newer and DeepSeek Harness `dsh-v0.2.2-rc.1`.
|
|
82
|
+
The bundle is published with all packages it depends on; nothing has to be
|
|
83
|
+
built or linked.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npm install -g @deepseek-ai/dsh@0.2.2-rc.1
|
|
87
|
+
dsh plugin --profile web add @4fu/dsh-arbor-bundle@2026.1110.1
|
|
88
|
+
dsh web
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`dsh plugin` adds the bundle to the profile's `dsh.profile.bundles` and installs
|
|
92
|
+
its packages into the profile. The packages declare DSH's runtime packages as
|
|
93
|
+
peer dependencies, so the profile keeps DSH's own copies rather than installing
|
|
94
|
+
a second set. Pick **Arbor** in a composer and send a message.
|
|
95
|
+
The optional [font plugin](https://github.com/4fuu/dsh-arbor-fonts) installs the
|
|
96
|
+
same way, but it is a Host row rather than a bundle. Add the row to the
|
|
97
|
+
profile's `cordis.patch.yml` after installing it:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
dsh plugin --profile web add @4fu/dsh-arbor-fonts@2026.1110.0
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
```yaml
|
|
104
|
+
- insert:
|
|
105
|
+
- id: arbor-fonts
|
|
106
|
+
name: '@4fu/dsh-arbor-fonts'
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Replace the default `[]` in that file with the entry, then restart the profile.
|
|
110
|
+
|
|
111
|
+
### From a checkout
|
|
112
|
+
|
|
113
|
+
The proofs and development use the TypeScript sources directly; that checkout is
|
|
114
|
+
what the scripts and the pin check run against.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
git clone https://github.com/4fuu/dsh-arbor
|
|
118
|
+
cd dsh-arbor
|
|
119
|
+
pnpm install --frozen-lockfile
|
|
120
|
+
pnpm check # tsc --noEmit over every package and proof
|
|
121
|
+
pnpm proofs # 29 proofs against the scripted model
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The first install may ask pnpm to approve dependency build scripts. Do not
|
|
125
|
+
approve them by hand: the repository's `pnpm-workspace.yaml` already denies the
|
|
126
|
+
six scripts it pulls in, each with the reason, and `pnpm install` finishes
|
|
127
|
+
cleanly with that policy.
|
|
128
|
+
|
|
129
|
+
### Hand the install to a model
|
|
130
|
+
|
|
131
|
+
[docs/install-for-agents.md](docs/install-for-agents.md) is a step-by-step guide
|
|
132
|
+
written for an AI agent: install from npm or a checkout, run the proofs, drive a
|
|
133
|
+
live turn with a restart, boot the web profile, and inspect what ran. Give a
|
|
134
|
+
model this prompt, editing the bracketed parts or dropping them:
|
|
135
|
+
|
|
136
|
+
```text copy Copy Install Prompt
|
|
137
|
+
Install dsh-arbor (https://github.com/4fuu/dsh-arbor) in this environment [in a
|
|
138
|
+
scratch DSH home under /tmp] and show me that it works: follow
|
|
139
|
+
docs/install-for-agents.md, install @4fu/dsh-arbor-bundle@2026.1110.1 into a DSH
|
|
140
|
+
web profile, pick Arbor for one session, and [also run the proof suite from a
|
|
141
|
+
checkout with pnpm install --frozen-lockfile, pnpm check and pnpm proofs]. Report
|
|
142
|
+
which checks passed, what the live session answered, and anything that did not
|
|
143
|
+
work. Fix only what is yours to fix and tell me what you changed.
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Use it in DSH
|
|
147
|
+
|
|
148
|
+
The npm install above adds the bundle to a DSH web profile. Pick **Arbor** in a
|
|
149
|
+
composer and send a message: the session is created under the preset, its window
|
|
150
|
+
and chain appear under the **Arbor** entry, and an ordinary session started
|
|
151
|
+
beside it keeps DSH's own composer and sandbox behavior. A checkout can also be
|
|
152
|
+
wired by hand; [section 7 of the install guide](docs/install-for-agents.md#7-wiring-the-bundle-into-a-real-dsh-installation)
|
|
153
|
+
shows the profile layout.
|
|
154
|
+
|
|
155
|
+
The one-shot `dsh <task>` runner cannot drive an arbor-preset session: DSH's
|
|
156
|
+
runner refuses to adopt a session that runs under a preset. Use the web client,
|
|
157
|
+
the SDK path the proofs use, or a remote `agent-preset/select` on a blank.
|
|
158
|
+
|
|
159
|
+
## Pinned DSH version
|
|
160
|
+
|
|
161
|
+
DSH is pinned to **`dsh-v0.2.2-rc.1`** (npm
|
|
162
|
+
`@deepseek-ai/dsh@0.2.2-rc.1`, commit `66491b05e3` of
|
|
163
|
+
`deepseek-ai/deepseek-harness`). Every `@deepseek-ai/dsh*` dependency is an
|
|
164
|
+
exact version, every package under `packages/` declares
|
|
165
|
+
`engines.dsh: 0.2.2-rc.1`, and `pnpm check:pin` checks both plus the prompt
|
|
166
|
+
bridge's `DSH_PINNED_VERSION`. DSH's runtime packages are peer dependencies, and
|
|
167
|
+
`pnpm check:profile` installs the packed packages into a fresh DSH web profile to
|
|
168
|
+
prove the profile keeps one copy and can create both kinds of session.
|
|
169
|
+
|
|
170
|
+
A different DSH version is unsupported. The bundle depends on DSH's row names,
|
|
171
|
+
its one-`AgentFactory` rule, its prompt-section registry, its client slot
|
|
172
|
+
registration and its exact Fetch registry; a move in any of them can fail the
|
|
173
|
+
boot, leave an official prompt section undecided, or stop the Arbor page from
|
|
174
|
+
registering. `proofs/12-official-sections.ts` and `proofs/18-picker-pin.ts`
|
|
175
|
+
are the behavioral checks. The canonical pin and the upgrade procedure are in
|
|
176
|
+
[Versions and compatibility](docs/versions.md).
|
|
177
|
+
|
|
178
|
+
## Optional: the font plugin
|
|
179
|
+
|
|
180
|
+
[dsh-arbor-fonts](https://github.com/4fuu/dsh-arbor-fonts) is a separate DSH
|
|
181
|
+
client plugin that changes only the typography of the Arbor page. It sets
|
|
182
|
+
ChillDIN Gothic for display labels and ChillDIN for numerals, leaves body text
|
|
183
|
+
and code on DSH's fonts, and does nothing on ordinary pages. It is optional and
|
|
184
|
+
installs into the same DSH profile:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
dsh plugin --profile web add @4fu/dsh-arbor-fonts@2026.1110.0
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The fonts are licensed under the SIL Open Font License 1.1; the plugin ships the
|
|
191
|
+
license text, per-file hashes and attribution. The plugin is a Host row rather
|
|
192
|
+
than a bundle: after installing it, add the `arbor-fonts` entry described under
|
|
193
|
+
[Install](#install) to `cordis.patch.yml` and restart the profile.
|
|
194
|
+
|
|
195
|
+
## Limits worth knowing
|
|
196
|
+
|
|
197
|
+
- The judge is off: a model-based judge cannot run through the DSH backend yet.
|
|
198
|
+
- The 12 bundle packages are published together at one version; install and
|
|
199
|
+
upgrade the bundle as a unit, because its packages name each other exactly.
|
|
200
|
+
- `ovnfs` is optional. Without it arbor uses the plain-directory workspace, and a
|
|
201
|
+
window no longer gets a copy-on-write checkout.
|
|
202
|
+
- The web page's numbers and layout are read from arbor's journal and API; it is
|
|
203
|
+
not a generic DSH dashboard, and it deliberately shows only arbor sessions.
|
|
204
|
+
- The prompt bridge, the UI and the picker filter are pinned to DSH's internals,
|
|
205
|
+
not to a stable public API. See [Versions and compatibility](docs/versions.md).
|
|
206
|
+
|
|
207
|
+
## Documentation
|
|
208
|
+
|
|
209
|
+
- [Installing and inspecting dsh-arbor](docs/install-for-agents.md) — the
|
|
210
|
+
agent-facing install, proof, live-turn, web-profile and wiring guide.
|
|
211
|
+
- [Versions and compatibility](docs/versions.md) — the DSH and arbor pins, and
|
|
212
|
+
what breaks on another DSH version.
|
|
213
|
+
- [B0 findings](docs/b0-findings.md), [B3 findings](docs/b3-findings.md) and
|
|
214
|
+
[B3b projects and sandbox](docs/b3b-projects-and-sandbox.md) — historical
|
|
215
|
+
records of the integration's decisions, with the DSH source locations each
|
|
216
|
+
one rests on. They describe earlier stages; the install guide is the current
|
|
217
|
+
status.
|
|
218
|
+
- [Layout and proofs](#layout) below for a quick map of the packages.
|
|
219
|
+
|
|
220
|
+
## Layout
|
|
221
|
+
|
|
222
|
+
```text
|
|
223
|
+
packages/
|
|
224
|
+
arbor-host/ one Arbor host per process: SQLite journal and notes,
|
|
225
|
+
settings, window lifecycle, workspace and environment
|
|
226
|
+
arbor-driver/ the routing AgentRegistry, AgentFactory, session agent,
|
|
227
|
+
inbox and stream mirrors, and the journal relay
|
|
228
|
+
arbor-llm/ the arbor AgentBackend over DSH's ctx.llm and ctx.tools
|
|
229
|
+
arbor-prompt/ <harness>, the <dsh> data block, and the section table
|
|
230
|
+
arbor-tools/ DSH tools as arbor external tools, same-name replacement
|
|
231
|
+
arbor-workspace/ arbor's workspace over the shared folder (ovnfs or dir)
|
|
232
|
+
arbor-env/ /env, with DSH's skills and MCP servers mounted read-only
|
|
233
|
+
arbor-bundle/ the installable patch that mounts the rows and UI
|
|
234
|
+
arbor-facts/ the read face behind the Arbor page and its Fetch routes
|
|
235
|
+
arbor-ui/ the Arbor page, session tabs, copy and composer gate
|
|
236
|
+
arbor-ui-workspace/
|
|
237
|
+
the per-preset hero project picker
|
|
238
|
+
arbor-preset/ the agent-preset row that puts Arbor in the roster
|
|
239
|
+
arbor-testkit/ the boot harness, memory journal and scripted model
|
|
240
|
+
patches/ overlay layers a proof applies above the shipped profile
|
|
241
|
+
proofs/ one runnable proof per integration point
|
|
242
|
+
docs/ install, versions, screenshots and historical findings
|
|
243
|
+
scripts/ the pin and profile checks, the release build and packing
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## License
|
|
247
|
+
|
|
248
|
+
MIT. See [LICENSE](LICENSE). The optional font plugin vendors fonts under the
|
|
249
|
+
SIL Open Font License 1.1; its repository carries the license text and
|
|
250
|
+
attribution.
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one Arbor host a DSH process runs.
|
|
3
|
+
*
|
|
4
|
+
* Arbor's point is one journal, one note store and one board across many
|
|
5
|
+
* sources, so a DSH process mounts exactly one `Arbor` host and every DSH user
|
|
6
|
+
* session is one source (one conversation target) of it. This row owns that
|
|
7
|
+
* host:
|
|
8
|
+
*
|
|
9
|
+
* - the durable storage under `DSH_HOME/arbor/` — `@4fu/arbor-journal-sqlite`
|
|
10
|
+
* for the journal and `@4fu/arbor-notes-sqlite` for the note store, both on
|
|
11
|
+
* `node:sqlite`, so nothing native has to build and an arbor window survives
|
|
12
|
+
* a DSH restart (the host rebuilds every running window from the journal in
|
|
13
|
+
* `Arbor.start`);
|
|
14
|
+
* - the DSH-backed `AgentBackend` (`@4fu/dsh-arbor-llm`), which runs each
|
|
15
|
+
* window's turns through `ctx.llm` and each tool call through
|
|
16
|
+
* `ctx.tools.execute`;
|
|
17
|
+
* - the two prompt plugins (`@4fu/dsh-arbor-prompt`): a trusted `<harness>`
|
|
18
|
+
* section this bridge writes, and a `<dsh>` data block for third-party
|
|
19
|
+
* material;
|
|
20
|
+
* - the deployment's persona, read out of DSH's own assembly once and merged
|
|
21
|
+
* into arbor's `<identity>` section, because it is the deployment's
|
|
22
|
+
* configuration rather than a section of its own;
|
|
23
|
+
* - the environment (`@4fu/dsh-arbor-env`): arbor's own under
|
|
24
|
+
* `DSH_HOME/arbor/env`, with DSH's skills and MCP servers mounted read-only
|
|
25
|
+
* beside them;
|
|
26
|
+
* - the workspace (`@4fu/dsh-arbor-workspace`): arbor's own, over the shared
|
|
27
|
+
* folder under `DSH_HOME/arbor` — ovnfs when its daemon starts, the plain
|
|
28
|
+
* directory workspace otherwise. For arbor sessions there is no DSH sandbox:
|
|
29
|
+
* arbor's tools run on the host the way they do in standalone arbor, and the
|
|
30
|
+
* only protection is arbor's own secret scrub;
|
|
31
|
+
* - the project bridge (`@4fu/dsh-arbor-workspace`): the shared folder's
|
|
32
|
+
* project subfolders are registered in DSH's own `workspaceRegistry`, so
|
|
33
|
+
* arbor's projects are the DSH project list a user picks from;
|
|
34
|
+
* - the `send` executor (`./send.ts`): a window's `send` lands on the DSH
|
|
35
|
+
* session log as an assistant message, which is the only surface the user
|
|
36
|
+
* reads;
|
|
37
|
+
* - the settings, whose window block is left to arbor's own defaults: a window
|
|
38
|
+
* stays open 300 s after its run ends and the next input for the session
|
|
39
|
+
* opens the next window of its chain.
|
|
40
|
+
*
|
|
41
|
+
* The judge is off on purpose in B1: every judge decision is model-based, and
|
|
42
|
+
* a model-based judge cannot run through the DSH backend yet. With the judge
|
|
43
|
+
* off, `host.dispatch` is deterministic — one window per session target,
|
|
44
|
+
* opened on the session's first turn — which is the routing policy B1 ships.
|
|
45
|
+
*
|
|
46
|
+
* There is no `workspaces.retentionMs` here: ovnfs checkouts are a window's own
|
|
47
|
+
* work, and a chain that ends leaves them where they are until a retention
|
|
48
|
+
* period is set, exactly as arbor's own defaults do. Nothing here is reclaimed
|
|
49
|
+
* by default, and the shared folder's projects are never removed — they are the
|
|
50
|
+
* user's work, not arbor's to delete.
|
|
51
|
+
*/
|
|
52
|
+
import { Service } from '@deepseek-ai/cordis';
|
|
53
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
54
|
+
import z from '@deepseek-ai/schemastery';
|
|
55
|
+
import type { Environment, Journal, NoteStore, Plugin, Settings, Target, WindowRef, Workspace } from '@4fu/arbor-interfaces';
|
|
56
|
+
import { Arbor } from '@4fu/arbor-core';
|
|
57
|
+
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
58
|
+
import { type DshBackend } from '@4fu/dsh-arbor-llm';
|
|
59
|
+
import { DshProjectBridge, type DshProject, type ExternalPointer, type ExternalProject } from '@4fu/dsh-arbor-workspace';
|
|
60
|
+
import type { WorkspaceBackend } from '@4fu/arbor-core';
|
|
61
|
+
export { DshSendExecutor, DSH_TARGET_PREFIX, DSH_USER_TARGET } from './send.js';
|
|
62
|
+
declare module '@deepseek-ai/cordis' {
|
|
63
|
+
interface Context {
|
|
64
|
+
/** The one Arbor host this DSH process runs. */
|
|
65
|
+
arborHost: ArborHostService;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** Plugin config: the data root and extra arbor settings, merged over the defaults. */
|
|
69
|
+
export interface Config {
|
|
70
|
+
/** Where the journal and the note store live. Defaults to `<DSH home>/arbor`. */
|
|
71
|
+
readonly root?: string;
|
|
72
|
+
/** Extra arbor settings, merged over {@link defaultSettings}. */
|
|
73
|
+
readonly settings?: Settings;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The settings this deployment runs arbor with.
|
|
77
|
+
*
|
|
78
|
+
* The identity is this bridge's own sentence, in Chinese like every other
|
|
79
|
+
* model-facing line dsh-arbor writes, and it says only what is true and not
|
|
80
|
+
* already said: `<harness>` states that the window runs inside DeepSeek
|
|
81
|
+
* Harness and that the user sees only what `send` writes, and arbor's own
|
|
82
|
+
* `<agent>` rules state that a window is disposable and that its chain
|
|
83
|
+
* continues. Nothing here repeats either, and nothing here claims one DSH
|
|
84
|
+
* session is one arbor window — several windows serve one source and each ends
|
|
85
|
+
* on its own.
|
|
86
|
+
*
|
|
87
|
+
* `generalRules` is deliberately absent: arbor's own defaults are the seven
|
|
88
|
+
* rules every window needs, in Chinese, and B0's English replacements dropped
|
|
89
|
+
* them and restated two of them wrongly.
|
|
90
|
+
*
|
|
91
|
+
* The window block is deliberately absent too: arbor's own defaults apply
|
|
92
|
+
* (300 s idle, 200 000 context tokens, 4 000 reserved), so a window that goes
|
|
93
|
+
* quiet ends and the session's next turn opens the next window of its chain.
|
|
94
|
+
*
|
|
95
|
+
* The workspaces block is deliberately absent as well: the workspace is arbor's
|
|
96
|
+
* own over the shared folder, ovnfs checkouts are a window's own work, and the
|
|
97
|
+
* shared folder's projects are never reclaimed — so with no retention period
|
|
98
|
+
* nothing is removed.
|
|
99
|
+
*/
|
|
100
|
+
export declare function defaultSettings(extra: Settings | undefined): Settings;
|
|
101
|
+
/** The target prefix that names the DSH session a window belongs to. */
|
|
102
|
+
export declare const SESSION_TARGET_PREFIX = "dsh:session:";
|
|
103
|
+
/** The conversation target one DSH user session is, for arbor's dispatch. */
|
|
104
|
+
export declare function sessionTarget(sessionId: string): Target;
|
|
105
|
+
/** The DSH session a conversation target names, or undefined for any other target. */
|
|
106
|
+
export declare function sessionOfTarget(target: Target): SessionId | undefined;
|
|
107
|
+
/**
|
|
108
|
+
* The plugin that records which DSH session each window serves.
|
|
109
|
+
*
|
|
110
|
+
* Arbor's `windowStart` hook runs before the `window_start` event is appended
|
|
111
|
+
* and before the window's prompt epoch is rendered, and it carries the window
|
|
112
|
+
* ref together with the task's targets — the only moment at which a window can
|
|
113
|
+
* be attributed to a session before its prefix is built. A prompt section that
|
|
114
|
+
* answers per session (the external-project pointers, the project line) is
|
|
115
|
+
* rendered for a window, and a window's prefix is frozen when it opens, so the
|
|
116
|
+
* attribution has to exist by then: reading it later would put those lines in a
|
|
117
|
+
* delta of the window's opening instead of in the prefix it opens with.
|
|
118
|
+
*
|
|
119
|
+
* The `windowContinue` hook is the same moment for a continuation window, which
|
|
120
|
+
* carries no targets of its own — a continuation continues its predecessor's
|
|
121
|
+
* work, so it serves the session that window served. Without it a window the
|
|
122
|
+
* host continues (the hard limit, an idle handover, a restart's recovery) opens
|
|
123
|
+
* with a prefix that names no session and loses both per-session lines.
|
|
124
|
+
*
|
|
125
|
+
* The hooks observe and change nothing, so a window they cannot attribute — one
|
|
126
|
+
* dispatched from outside a DSH session — opens exactly as it would without them.
|
|
127
|
+
*
|
|
128
|
+
* @param note - called with the window and the session it serves.
|
|
129
|
+
* @param sessionOf - the session a window already recorded serves, for a
|
|
130
|
+
* continuation to inherit.
|
|
131
|
+
*/
|
|
132
|
+
export declare function windowSessionPlugin(note: (window: WindowRef, sessionId: SessionId) => void, sessionOf: (window: WindowRef) => SessionId | undefined): Plugin;
|
|
133
|
+
/** The one Arbor host per DSH process, and the storage it runs on. */
|
|
134
|
+
export declare class ArborHostService extends Service {
|
|
135
|
+
static inject: string[];
|
|
136
|
+
static Config: z<Config, Config>;
|
|
137
|
+
readonly config: Config;
|
|
138
|
+
private state;
|
|
139
|
+
/**
|
|
140
|
+
* Which DSH session each window serves, written by the `windowStart` hook
|
|
141
|
+
* before the window's prefix is built. Arbor's own `WindowRef` carries no
|
|
142
|
+
* session identity, and a per-session prompt section is rendered for a window,
|
|
143
|
+
* so this is the mapping that section reads.
|
|
144
|
+
*/
|
|
145
|
+
private readonly windowSessions;
|
|
146
|
+
/**
|
|
147
|
+
* The stored cwd of a session whose agent is not live, read once. A session's
|
|
148
|
+
* stored header never moves, so the answer is remembered; the live projection
|
|
149
|
+
* takes over the moment the session is back.
|
|
150
|
+
*/
|
|
151
|
+
private readonly storedCwds;
|
|
152
|
+
/** Raised whenever a DSH prompt provider changes, so both sections follow. */
|
|
153
|
+
private promptChanges;
|
|
154
|
+
/** Plan mode's projection, so the `<harness>` section can read it per step. */
|
|
155
|
+
private planProjection;
|
|
156
|
+
/** The deployment's persona, read once when the host is built. */
|
|
157
|
+
private persona;
|
|
158
|
+
/** The last provider and model a live session rendered under, for the fallback. */
|
|
159
|
+
private lastProvider;
|
|
160
|
+
private lastModel;
|
|
161
|
+
/** The warnings the bridge raised while the host was built, for diagnostics. */
|
|
162
|
+
readonly warnings: string[];
|
|
163
|
+
constructor(ctx: Context, config?: Config);
|
|
164
|
+
protected [Service.init](): Promise<void>;
|
|
165
|
+
/**
|
|
166
|
+
* The dynamic state the `<harness>` section renders, read for the session
|
|
167
|
+
* whose turn is running.
|
|
168
|
+
*
|
|
169
|
+
* A section is frozen per epoch and shared by every window of it, so a
|
|
170
|
+
* session's own policy is the one whose turn is rendering — the same rule
|
|
171
|
+
* arbor's `<workspace>` section follows for its root. A window a restart
|
|
172
|
+
* continued renders outside any turn and therefore without these lines until
|
|
173
|
+
* its session's next turn moves the revision and delivers them as a delta.
|
|
174
|
+
*/
|
|
175
|
+
private harnessState;
|
|
176
|
+
/** The plan-mode state of one session, from the host's own registration. */
|
|
177
|
+
private planReader;
|
|
178
|
+
/**
|
|
179
|
+
* Note that a DSH prompt input changed without a `system-prompt/change`.
|
|
180
|
+
*
|
|
181
|
+
* The driver calls this after it installs the arbor tool restriction in a
|
|
182
|
+
* session's scope: a `tool:*` section's text reads whether its tool is
|
|
183
|
+
* registered, and the restriction moves that without a registration change.
|
|
184
|
+
*/
|
|
185
|
+
noteDshPromptChange(): void;
|
|
186
|
+
/**
|
|
187
|
+
* The deployment's persona lines, with DSH's prompt variables resolved
|
|
188
|
+
* against the session whose turn is rendering.
|
|
189
|
+
*
|
|
190
|
+
* A window a restart continued renders outside any turn, so the last values
|
|
191
|
+
* a live session gave are the fallback: the persona then names the model that
|
|
192
|
+
* session was running rather than carrying a placeholder.
|
|
193
|
+
*/
|
|
194
|
+
private personaLines;
|
|
195
|
+
private requireState;
|
|
196
|
+
/** The one Arbor host of this process. */
|
|
197
|
+
get host(): Arbor;
|
|
198
|
+
/** The durable journal every window of this process records into. */
|
|
199
|
+
get journal(): Journal;
|
|
200
|
+
/** The durable note store beside the journal. */
|
|
201
|
+
get notes(): NoteStore;
|
|
202
|
+
/** The settings this host runs arbor with. */
|
|
203
|
+
get settings(): Settings;
|
|
204
|
+
/**
|
|
205
|
+
* Arbor's own workspace over the shared folder: the implementation every
|
|
206
|
+
* window's files go through, and the one a read-only surface asks for the
|
|
207
|
+
* chain's checkouts, keeps and open work.
|
|
208
|
+
*/
|
|
209
|
+
get workspace(): Workspace;
|
|
210
|
+
/** The environment every window reads at `/env`, DSH's items mounted beside arbor's. */
|
|
211
|
+
get environment(): Environment;
|
|
212
|
+
/** The arbor shared folder: the projects' home and ovnfs's source. */
|
|
213
|
+
get sharedFolder(): string;
|
|
214
|
+
/** Which workspace backend arbor settled on: `ovnfs` or `dir`. */
|
|
215
|
+
get workspaceBackend(): WorkspaceBackend;
|
|
216
|
+
/** The DSH project bridge over the shared folder. */
|
|
217
|
+
get projects(): DshProjectBridge;
|
|
218
|
+
/**
|
|
219
|
+
* The DSH session whose work this window runs, or undefined while nothing
|
|
220
|
+
* attributes it. A window a restart continued before its session came back
|
|
221
|
+
* answers undefined until the session resumes and its next turn opens the
|
|
222
|
+
* window's own chain entry; the pointer section then reads no pointers rather
|
|
223
|
+
* than another session's.
|
|
224
|
+
*/
|
|
225
|
+
sessionOfWindow(window: WindowRef): SessionId | undefined;
|
|
226
|
+
/**
|
|
227
|
+
* The project of the shared folder the named window's session works in, or
|
|
228
|
+
* undefined when it works in none.
|
|
229
|
+
*
|
|
230
|
+
* The project is read from the session's own current directory, which the
|
|
231
|
+
* driver resolves into the shared folder for every arbor session — created
|
|
232
|
+
* under arbor, re-routed onto it, or resumed after a restart — so a session the
|
|
233
|
+
* user opened on a project and a session that picked arbor afterwards answer
|
|
234
|
+
* the same way.
|
|
235
|
+
*/
|
|
236
|
+
projectOfWindow(window: WindowRef | undefined): Promise<ExternalProject | undefined>;
|
|
237
|
+
/**
|
|
238
|
+
* The directory one session works in, the way DSH answers it.
|
|
239
|
+
*
|
|
240
|
+
* The `workingDirectory` projection is DSH's own answer for a session's
|
|
241
|
+
* current directory — the cwd display, the session listing and the skill
|
|
242
|
+
* catalog all read it — while the session header carries the directory the
|
|
243
|
+
* session was created with and never moves. A session whose directory changed
|
|
244
|
+
* therefore names the new one, which is what the project line has to say: the
|
|
245
|
+
* alternative is a prompt that names a project the UI no longer shows.
|
|
246
|
+
*
|
|
247
|
+
* A window recovered before its session resumed has no live agent to read —
|
|
248
|
+
* the host continues it at boot, long before the user opens the session again
|
|
249
|
+
* — and its prefix is frozen then. The stored session header is the durable
|
|
250
|
+
* answer for that moment, the same one the driver resolves a resume from, so
|
|
251
|
+
* the recovered window opens naming the project it was opened on. It is read
|
|
252
|
+
* once per session and remembered: a session's stored header never moves, and
|
|
253
|
+
* the live answer takes over the moment the session is back.
|
|
254
|
+
*/
|
|
255
|
+
/**
|
|
256
|
+
* The directory one session works in, the way the `<workspace>` section reads
|
|
257
|
+
* it: DSH's `workingDirectory` projection when the session is live, the
|
|
258
|
+
* stored header otherwise. A read-only surface that names the session's
|
|
259
|
+
* project asks this rather than re-deriving it.
|
|
260
|
+
*/
|
|
261
|
+
directoryOf(sessionId: SessionId): Promise<string | undefined>;
|
|
262
|
+
private directoryOfSession;
|
|
263
|
+
/** The cwd a persisted session was created with, or undefined without a store. */
|
|
264
|
+
private storedCwdOf;
|
|
265
|
+
/**
|
|
266
|
+
* A revision that moves whenever anything the `<workspace>` section names
|
|
267
|
+
* moves: any session's pointers, or any session's project. The host asks for it
|
|
268
|
+
* once per step, so a window whose session picks another project before its
|
|
269
|
+
* first turn is handed a delta of only the project line, and a session that
|
|
270
|
+
* gains or loses a pointer is handed a delta of only that line.
|
|
271
|
+
*/
|
|
272
|
+
private workspaceRevision;
|
|
273
|
+
/**
|
|
274
|
+
* Every session the `<workspace>` section answers for: the live ones, plus the
|
|
275
|
+
* ones a recovered window still names — a window the host continued at boot
|
|
276
|
+
* belongs to a session whose agent is not back yet, and its project is part of
|
|
277
|
+
* what the section says.
|
|
278
|
+
*/
|
|
279
|
+
private sessionProjects;
|
|
280
|
+
/**
|
|
281
|
+
* Where one arbor session works: a project folder inside the shared folder.
|
|
282
|
+
*
|
|
283
|
+
* The driver calls this for an arbor session's requested cwd and sets the
|
|
284
|
+
* session's cwd to the answer, so the DSH session, its cwd display and the
|
|
285
|
+
* project picker all agree on the shared folder. A directory outside the
|
|
286
|
+
* shared folder is not copied; it joins the shared folder only through
|
|
287
|
+
* {@link adoptArborProject}.
|
|
288
|
+
*/
|
|
289
|
+
resolveArborCwd(requested: string | undefined): Promise<string>;
|
|
290
|
+
/**
|
|
291
|
+
* Whether the user added this directory through DSH's own add-workspace
|
|
292
|
+
* action, outside the shared folder.
|
|
293
|
+
*
|
|
294
|
+
* This is what separates a directory the user means arbor to name from a cwd a
|
|
295
|
+
* session merely inherited: the add-workspace action is the one path in DSH
|
|
296
|
+
* that registers a directory of the user's choosing, so it is the only one
|
|
297
|
+
* that may turn that directory into a pointer. The driver asks before it
|
|
298
|
+
* records one.
|
|
299
|
+
*/
|
|
300
|
+
isAddedArborDirectory(path: string): boolean;
|
|
301
|
+
/**
|
|
302
|
+
* Record an existing directory as an external pointer for one session: no
|
|
303
|
+
* copy, no link, no adoption, never a registered project. The owner's decision
|
|
304
|
+
* for an existing DSH project with its own path — the session works in arbor's
|
|
305
|
+
* own directory (the shared folder) and the user's original directory is named
|
|
306
|
+
* in arbor's prompt as a pointer, so the model reaches it directly. The driver
|
|
307
|
+
* calls it for a session that became an arbor session while naming an added
|
|
308
|
+
* directory; the model calls it for one it was asked about.
|
|
309
|
+
*/
|
|
310
|
+
pointerArborProject(path: string, sessionId: SessionId): ExternalPointer;
|
|
311
|
+
/**
|
|
312
|
+
* Make one arbor session a member of the workspace that owns the directory it
|
|
313
|
+
* works in, so DSH's own hero composer has a workspace to name and stays live.
|
|
314
|
+
*
|
|
315
|
+
* The driver calls this for every arbor session it creates or re-routes, with
|
|
316
|
+
* the cwd it resolved for it: an arbor session works in arbor's own directory,
|
|
317
|
+
* and DSH filters a workspace's member list by the session's stored cwd, so
|
|
318
|
+
* the session has to be a member of the workspace whose path is that
|
|
319
|
+
* directory. See {@link DshProjectBridge.attachWorkspace} for why the composer
|
|
320
|
+
* is inert without it.
|
|
321
|
+
*/
|
|
322
|
+
attachArborWorkspace(sessionId: SessionId, cwd: string): Promise<void>;
|
|
323
|
+
/** Drop a recorded external pointer by name for one session. */
|
|
324
|
+
removeArborPointer(name: string, sessionId: SessionId): void;
|
|
325
|
+
/**
|
|
326
|
+
* Every external pointer one session recorded whose directory still exists,
|
|
327
|
+
* true at read. This is what that session's `<workspace>` prompt section
|
|
328
|
+
* names, and only that session's.
|
|
329
|
+
*/
|
|
330
|
+
listArborPointers(sessionId: SessionId): Promise<readonly ExternalPointer[]>;
|
|
331
|
+
/** Every project registered from the shared folder, as DSH lists them. */
|
|
332
|
+
listArborProjects(): Promise<readonly DshProject[]>;
|
|
333
|
+
/** The backend every window of this process runs through. */
|
|
334
|
+
get backend(): DshBackend;
|
|
335
|
+
/** Write the sends a step delivered, after all its tool results are on the log. */
|
|
336
|
+
flushSends(): void;
|
|
337
|
+
/** The directory the journal and the note store live in. */
|
|
338
|
+
get dataRoot(): string;
|
|
339
|
+
/**
|
|
340
|
+
* Every window that serves this session, oldest first.
|
|
341
|
+
*
|
|
342
|
+
* A window carries the session's target in its own `window_start`, and a
|
|
343
|
+
* continuation carries its predecessor instead; a chain therefore starts at a
|
|
344
|
+
* target-carrying window and every continuation leads back to one. A session
|
|
345
|
+
* can have several such roots — one per turn after an idle window ended — and
|
|
346
|
+
* each root's continuations belong to the same session. The journal's start
|
|
347
|
+
* order is what orders them all, and it is what the relay reads the chain's
|
|
348
|
+
* tool calls back out of on resume.
|
|
349
|
+
*/
|
|
350
|
+
windowChainOf(sessionId: SessionId): Promise<WindowRef[]>;
|
|
351
|
+
/**
|
|
352
|
+
* The pre-label chain walk: the session's target names its first windows and
|
|
353
|
+
* each continuation names its predecessor. Only a journal with no labels
|
|
354
|
+
* reaches this; a current one answers by label.
|
|
355
|
+
*/
|
|
356
|
+
private windowChainByTarget;
|
|
357
|
+
/**
|
|
358
|
+
* Rebuild which session every recorded window serves, from the journal.
|
|
359
|
+
*
|
|
360
|
+
* The two hooks attribute a window the moment it opens — `windowStart` from
|
|
361
|
+
* the task's targets, `windowContinue` from the window it continues — and that
|
|
362
|
+
* map is process memory: it died with the process that wrote it. The journal
|
|
363
|
+
* is the durable record of the same fact, because every `window_start` carries
|
|
364
|
+
* the session's target and a continuation names its predecessor in its body.
|
|
365
|
+
* So the attribution is rebuilt here, before the host starts, so that a window
|
|
366
|
+
* the recovery continues opens with a prefix that still names its session.
|
|
367
|
+
*
|
|
368
|
+
* A window no chain leads back to a target — one dispatched from outside a DSH
|
|
369
|
+
* session — is left unattributed, which is the answer the hooks give it too.
|
|
370
|
+
*/
|
|
371
|
+
private rebuildWindowSessions;
|
|
372
|
+
/**
|
|
373
|
+
* The window this session's work is running in, when it has a live one.
|
|
374
|
+
*
|
|
375
|
+
* The journal is the record of which window belongs to which session: every
|
|
376
|
+
* `window_start` carries the label its task named, arbor carries that label
|
|
377
|
+
* onto the window's continuations, and a window a restart stopped is continued
|
|
378
|
+
* under a new ref with the same label. So the newest labeled start names the
|
|
379
|
+
* session's window, and it is the live one unless the chain has ended.
|
|
380
|
+
*/
|
|
381
|
+
windowFor(sessionId: SessionId): Promise<WindowRef | undefined>;
|
|
382
|
+
}
|
|
383
|
+
export default ArborHostService;
|