super-dsh 0.1.0 → 0.1.2
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/agent-agy/bridge/agy_bridge.py +250 -0
- package/agent-agy/bridge/gemini_openai_server.mjs +98 -0
- package/agent-agy/cordis.patch.yml +49 -0
- package/agent-agy/dist/adapter.d.ts +15 -0
- package/agent-agy/dist/adapter.js +42 -0
- package/agent-agy/dist/agent-preset-agy.d.ts +47 -0
- package/agent-agy/dist/agent-preset-agy.js +169 -0
- package/agent-agy/dist/agent-preset-projection.d.ts +31 -0
- package/agent-agy/dist/agent-preset-projection.js +33 -0
- package/agent-agy/dist/agent.d.ts +137 -0
- package/agent-agy/dist/agent.js +493 -0
- package/agent-agy/dist/agy-cli-client.d.ts +137 -0
- package/agent-agy/dist/agy-cli-client.js +301 -0
- package/agent-agy/dist/agy-client.d.ts +160 -0
- package/agent-agy/dist/agy-client.js +252 -0
- package/agent-agy/dist/agy-sessions.d.ts +24 -0
- package/agent-agy/dist/agy-sessions.js +116 -0
- package/agent-agy/dist/agy-store.d.ts +39 -0
- package/agent-agy/dist/agy-store.js +83 -0
- package/agent-agy/dist/inbox.d.ts +26 -0
- package/agent-agy/dist/inbox.js +53 -0
- package/agent-agy/dist/index.d.ts +80 -0
- package/agent-agy/dist/index.js +505 -0
- package/agent-agy/dist/models.d.ts +41 -0
- package/agent-agy/dist/models.js +74 -0
- package/agent-agy/dist/world-plugin.d.ts +9 -0
- package/agent-agy/dist/world-plugin.js +97 -0
- package/agent-agy/package.json +64 -0
- package/agent-claude/cordis.patch.yml +42 -0
- package/agent-claude/dist/adapter.js +62 -0
- package/agent-claude/dist/agent-preset-claude.js +169 -0
- package/agent-claude/dist/agent-preset-projection.js +30 -0
- package/agent-claude/dist/agent.js +1271 -0
- package/agent-claude/dist/claude-client.js +357 -0
- package/agent-claude/dist/claude-events.js +223 -0
- package/agent-claude/dist/claude-home.js +29 -0
- package/agent-claude/dist/commands.js +182 -0
- package/agent-claude/dist/content.js +134 -0
- package/agent-claude/dist/inbox.js +53 -0
- package/agent-claude/dist/index.js +555 -0
- package/agent-claude/dist/input-queue.js +64 -0
- package/agent-claude/dist/interaction.js +271 -0
- package/agent-claude/dist/models.js +158 -0
- package/agent-claude/dist/permission.js +120 -0
- package/agent-claude/dist/session-id.js +20 -0
- package/agent-claude/dist/session-map.js +165 -0
- package/agent-claude/dist/world-plugin.js +14 -0
- package/agent-claude/package.json +74 -0
- package/agent-codex/README.md +74 -0
- package/agent-codex/cordis.patch.yml +77 -0
- package/agent-codex/dist/adapter.js +55 -0
- package/agent-codex/dist/agent-preset-codex.js +171 -0
- package/agent-codex/dist/agent-preset-projection.js +37 -0
- package/agent-codex/dist/agent.js +1056 -0
- package/agent-codex/dist/app-home.js +65 -0
- package/agent-codex/dist/codex-client.js +469 -0
- package/agent-codex/dist/codex-events.js +297 -0
- package/agent-codex/dist/codex-store.js +190 -0
- package/agent-codex/dist/inbox.js +53 -0
- package/agent-codex/dist/index.js +605 -0
- package/agent-codex/dist/models.js +152 -0
- package/agent-codex/dist/permission.js +71 -0
- package/agent-codex/dist/session-map.js +170 -0
- package/agent-codex/dist/spike.js +46 -0
- package/agent-codex/dist/world-plugin.js +96 -0
- package/agent-codex/package.json +78 -0
- package/agent-hermes/README.md +144 -0
- package/agent-hermes/cordis.patch.yml +83 -0
- package/agent-hermes/dist/adapter.js +83 -0
- package/agent-hermes/dist/agent-preset-hermes.js +171 -0
- package/agent-hermes/dist/agent-preset-projection.js +34 -0
- package/agent-hermes/dist/agent.js +1068 -0
- package/agent-hermes/dist/hermes-client.js +953 -0
- package/agent-hermes/dist/hermes-events.js +289 -0
- package/agent-hermes/dist/hermes-store.js +158 -0
- package/agent-hermes/dist/inbox.js +53 -0
- package/agent-hermes/dist/index.js +541 -0
- package/agent-hermes/dist/models.js +324 -0
- package/agent-hermes/dist/permission.js +60 -0
- package/agent-hermes/dist/world-plugin.js +101 -0
- package/agent-hermes/package.json +76 -0
- package/agent-hub/cordis.patch.yml +12 -0
- package/agent-hub/dist/agent-roster.d.ts +34 -0
- package/agent-hub/dist/agent-roster.js +27 -0
- package/agent-hub/dist/carrier.d.ts +43 -0
- package/agent-hub/dist/carrier.js +241 -0
- package/agent-hub/dist/client-shim.d.ts +28 -0
- package/agent-hub/dist/client-shim.js +251 -0
- package/agent-hub/dist/envelope.d.ts +54 -0
- package/agent-hub/dist/envelope.js +92 -0
- package/agent-hub/dist/gateway.d.ts +28 -0
- package/agent-hub/dist/gateway.js +18 -0
- package/agent-hub/dist/index-pass.d.ts +22 -0
- package/agent-hub/dist/index-pass.js +47 -0
- package/agent-hub/dist/index.d.ts +29 -0
- package/agent-hub/dist/index.js +29 -0
- package/agent-hub/dist/labels.d.ts +45 -0
- package/agent-hub/dist/labels.js +83 -0
- package/agent-hub/dist/ownership.d.ts +37 -0
- package/agent-hub/dist/ownership.js +54 -0
- package/agent-hub/dist/roster.d.ts +28 -0
- package/agent-hub/dist/roster.js +38 -0
- package/agent-hub/dist/rpc.d.ts +38 -0
- package/agent-hub/dist/rpc.js +52 -0
- package/agent-hub/dist/spawn-world.d.ts +46 -0
- package/agent-hub/dist/spawn-world.js +72 -0
- package/agent-hub/dist/targets.d.ts +33 -0
- package/agent-hub/dist/targets.js +30 -0
- package/agent-hub/dist/world-entry.d.ts +31 -0
- package/agent-hub/dist/world-entry.js +51 -0
- package/agent-hub/dist/world-host.d.ts +69 -0
- package/agent-hub/dist/world-host.js +44 -0
- package/agent-hub/dist/world-join.d.ts +16 -0
- package/agent-hub/dist/world-join.js +122 -0
- package/agent-hub/dist/world-mount.d.ts +52 -0
- package/agent-hub/dist/world-mount.js +52 -0
- package/agent-hub/dist/world-mux.d.ts +72 -0
- package/agent-hub/dist/world-mux.js +224 -0
- package/agent-hub/dist/world-provision.d.ts +20 -0
- package/agent-hub/dist/world-provision.js +122 -0
- package/agent-hub/dist/world-web-server.d.ts +162 -0
- package/agent-hub/dist/world-web-server.js +248 -0
- package/agent-hub/lib/client/index.js +242 -0
- package/agent-hub/package.json +65 -0
- package/agent-omp/README.md +77 -0
- package/agent-omp/cordis.patch.yml +92 -0
- package/agent-omp/dist/adapter.js +36 -0
- package/agent-omp/dist/agent-preset-omp.js +171 -0
- package/agent-omp/dist/agent-preset-projection.js +36 -0
- package/agent-omp/dist/agent.js +1145 -0
- package/agent-omp/dist/app-home.js +86 -0
- package/agent-omp/dist/discovery.js +116 -0
- package/agent-omp/dist/inbox.js +53 -0
- package/agent-omp/dist/index.js +574 -0
- package/agent-omp/dist/knobs.js +50 -0
- package/agent-omp/dist/lazy-rpc.js +161 -0
- package/agent-omp/dist/mobile/gesture.js +148 -0
- package/agent-omp/dist/mobile/zoom-guard.js +294 -0
- package/agent-omp/dist/mobile-boot.js +38 -0
- package/agent-omp/dist/models.js +299 -0
- package/agent-omp/dist/omp-cli.js +147 -0
- package/agent-omp/dist/omp-disk-discovery.js +178 -0
- package/agent-omp/dist/omp-store.js +518 -0
- package/agent-omp/dist/pairing.js +45 -0
- package/agent-omp/dist/permission.js +96 -0
- package/agent-omp/dist/protocol.js +13 -0
- package/agent-omp/dist/rpc-types.js +1 -0
- package/agent-omp/dist/sdk-client.js +378 -0
- package/agent-omp/dist/sidecar-client.js +195 -0
- package/agent-omp/dist/store/adapter.js +47 -0
- package/agent-omp/dist/store/db.js +210 -0
- package/agent-omp/dist/store/index.js +59 -0
- package/agent-omp/dist/store/reconcile.js +51 -0
- package/agent-omp/dist/store/schema.js +40 -0
- package/agent-omp/dist/world-plugin.js +96 -0
- package/agent-omp/package.json +97 -0
- package/agent-omp/sidecar/main.ts +481 -0
- package/agent-pi/README.md +67 -0
- package/agent-pi/cordis.patch.yml +80 -0
- package/agent-pi/dist/adapter.js +53 -0
- package/agent-pi/dist/agent-preset-pi.js +169 -0
- package/agent-pi/dist/agent-preset-projection.js +29 -0
- package/agent-pi/dist/agent.js +949 -0
- package/agent-pi/dist/inbox.js +53 -0
- package/agent-pi/dist/index.js +595 -0
- package/agent-pi/dist/knobs.js +24 -0
- package/agent-pi/dist/models.js +200 -0
- package/agent-pi/dist/permission.js +66 -0
- package/agent-pi/dist/pi-client.js +224 -0
- package/agent-pi/dist/pi-events.js +155 -0
- package/agent-pi/dist/pi-home.js +55 -0
- package/agent-pi/dist/safe-json.js +25 -0
- package/agent-pi/dist/session-map.js +119 -0
- package/agent-pi/dist/world-plugin.js +97 -0
- package/agent-pi/package.json +77 -0
- package/cordis.patch.yml +22 -19
- package/package.json +88 -25
- package/client.js +0 -4
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# @pgmi-builds/agent-adapter-hermes
|
|
2
|
+
|
|
3
|
+
Hermes provider for DeepSeek Harness (dsh) — an AgentFactory that embeds the
|
|
4
|
+
Nous Research Hermes Agent via the TUI gateway JSON-RPC (`python -m
|
|
5
|
+
tui_gateway.entry`) and bridges its session surface into Dash Agent/Session
|
|
6
|
+
contracts (Agent Worlds AW-H; parity round 2026-09-17 = Task M1 of
|
|
7
|
+
`docs/superpowers/plans/2026-09-17-aw-five-adapter-parity-fixes.md`).
|
|
8
|
+
|
|
9
|
+
## Layout
|
|
10
|
+
|
|
11
|
+
- `src/models.ts` — gateway `model.options` → Dash model catalog (trim rules,
|
|
12
|
+
r4 id dedupe, provider-name cache, slug list). Pure + fail-soft.
|
|
13
|
+
- `src/adapter.ts` — `HermesLlmAdapter`: one route per real gateway provider
|
|
14
|
+
slug; `providerInfo` display names; ids stay verbatim selection strings.
|
|
15
|
+
- `src/index.ts` — `HermesProvider` (AgentFactory): creation transaction,
|
|
16
|
+
boot route registration + atomic swap, default-model push, session map.
|
|
17
|
+
- `src/agent.ts` — `HermesAgent`: wire→DSH event bridge, usage conversion,
|
|
18
|
+
turn-error classification, resume self-heal, title/todo mirroring.
|
|
19
|
+
- `src/hermes-events.ts` — PURE gateway-event → omp-wire projector.
|
|
20
|
+
- `src/hermes-client.ts` — one client = one gateway child = one session
|
|
21
|
+
(spawn/env/ready/turn-settle/approval bridge; liveness getters; adoption
|
|
22
|
+
listener; opt-in idle-probe reaper).
|
|
23
|
+
- `src/hermes-store.ts` — `<dshHome>/agents/hermes/dsh-sessions.json` map.
|
|
24
|
+
- `test/` — imports `dist/`; build first (`npm run build`), then
|
|
25
|
+
`node --test test/*.test.mjs`. The world-plugin smoke needs the repo farm:
|
|
26
|
+
run with `DSH_HOME=<repo>/.tests` (it refuses prod homes).
|
|
27
|
+
|
|
28
|
+
## Per-adapter rulings (2026-09-17 M1; recorded per dev-rules §4/§12)
|
|
29
|
+
|
|
30
|
+
1. **Model grouping by real provider slug** (RC-3 fix). Boot registers the
|
|
31
|
+
single placeholder route `hermes` (the selector stays alive while the async
|
|
32
|
+
probe runs); when the first `model.options` probe resolves, the
|
|
33
|
+
registration atomically swaps to the DISTINCT surviving provider slugs
|
|
34
|
+
(`AdapterRegistrationHandle.replace` — validated in full, one synchronous
|
|
35
|
+
section). `listModels(provider)` filters by the entry's own `provider`;
|
|
36
|
+
`resolveModel` matches the (provider, id) pair. Model ids are NEVER
|
|
37
|
+
reformatted: they are the gateway's verbatim selection strings
|
|
38
|
+
(`session.create {model}` / `/model <id>` consume them as-is — wire
|
|
39
|
+
contract). The default-model push and the per-session route pin
|
|
40
|
+
(`model/selection`, `request/context`) carry `{provider: slug, model: id}`,
|
|
41
|
+
canonicalized through the catalog entry (the r4 dedupe gives every id ONE
|
|
42
|
+
provider route, so a pinned route always resolves).
|
|
43
|
+
2. **Catalog trims** (the "579-model wall" fix), applied at mapping time in
|
|
44
|
+
`mapHermesModelOptions`:
|
|
45
|
+
- `source: "virtual"` rows drop (the `moa` synthesizer is not a selectable
|
|
46
|
+
endpoint);
|
|
47
|
+
- mirrored endpoints with IDENTICAL model-id sets collapse onto one row —
|
|
48
|
+
first in gateway order wins, except a later `is_current` mirror displaces
|
|
49
|
+
a non-current winner (fixture: `copilot`/`copilot-acp`,
|
|
50
|
+
`zai-plan`/`zhipu`; 17 providers → 14 groups, 579 → 526 listed ids);
|
|
51
|
+
- endpoints with MORE than 50 models reorder `featured_models` first
|
|
52
|
+
(gateway order), remainder alphabetically; smaller rows keep gateway
|
|
53
|
+
order (the 47-model openrouter row is untouched).
|
|
54
|
+
The `authenticated !== true` filter predates this ruling and is unchanged.
|
|
55
|
+
3. **Cross-provider id dedupe stays (r4)**: an id listed under several slugs
|
|
56
|
+
keeps ONE canonical route (first surviving row, current-row preferred).
|
|
57
|
+
Rationale: the wire sends only the model id, so a duplicate listing would
|
|
58
|
+
fabricate provider-specific selection the gateway cannot honor; canonical
|
|
59
|
+
routes keep every pinned route resolvable. Consequence: multi-homed ids
|
|
60
|
+
(e.g. `kimi-k3` under copilot/qwen-cn/kimi-cn) list under their canonical
|
|
61
|
+
route only.
|
|
62
|
+
4. **`todo/write`** (typed transcript): the gateway's whole-list
|
|
63
|
+
`todo.updated {todos, revision}` snapshot → wire `todo_updated` (projector;
|
|
64
|
+
DSH-shaped items: trimmed content, status ∈ {pending, in_progress,
|
|
65
|
+
completed}; `cancelled`/unknown statuses and duplicate/blank content drop —
|
|
66
|
+
the DSH `todo/write` invariant has no cancelled status and forbids
|
|
67
|
+
repeats) → the agent appends the log-only `todo/write {todos}` snapshot,
|
|
68
|
+
only inside an open turn (upstream invariant), latest-wins. Log-only
|
|
69
|
+
semantics kept (upstream `tool-todo` contract); the todo tool pair still
|
|
70
|
+
renders through `tool_execution_*` as before.
|
|
71
|
+
5. **`session/title` mirror**: gateway `session.title {session_id, title}` →
|
|
72
|
+
wire `session_title` (pi wire parity) → `session/title
|
|
73
|
+
{title, messageSeqs: [], source:{kind:"provider", provider:"hermes"}}`,
|
|
74
|
+
deduped, fail-soft, never affects the turn. The client-side `session.info`
|
|
75
|
+
title capture stays (feeds `session.create`'s title + tracing).
|
|
76
|
+
6. **Turn usage** (RC-6 fix). Fixture verdict (`gateway-events.sample.json`
|
|
77
|
+
index 13): the gateway's `total` is CONTEXT-WIDE, not per-message —
|
|
78
|
+
`total 26805 = prompt 26803 + completion 2` and `prompt === context_used`
|
|
79
|
+
(the whole re-sent window), so trusting it would inflate every turn.
|
|
80
|
+
`convertUsage` therefore synthesizes the per-attempt-provable
|
|
81
|
+
`totalTokens = inputTokens + outputTokens` (fixture: 19251 + 2 = 19253),
|
|
82
|
+
which the DSH token-meter fold accepts (`normalizeUsage` needs `totalTokens`
|
|
83
|
+
or BOTH cache buckets; the gateway shape has none — the pre-fix sample
|
|
84
|
+
folded to `undefined` every turn). `reasoningTokens` rides only when
|
|
85
|
+
`reasoning ≤ output` (Dash: reasoning ⊆ output; the fold rejects larger
|
|
86
|
+
values). No cache tokens are invented (the gateway reports none —
|
|
87
|
+
`cache_hit_pct` is a percentage).
|
|
88
|
+
7. **Resume self-heal over lazy spawn** (RC-4 fix, ruling 4 of the plan; full
|
|
89
|
+
lazy spawn deferred):
|
|
90
|
+
- The client exposes `usable` / `closed` / `failure` liveness getters.
|
|
91
|
+
- On a CLIENT-TERMINAL failure (child exit, ready timeout, close — the
|
|
92
|
+
client's `usable` is false) the agent's failure path — and the cold-start
|
|
93
|
+
path — triggers the full idle-exit dispose (`#onIdleExit`), so the host
|
|
94
|
+
unregisters the dead agent and re-resumes a FRESH client on the next
|
|
95
|
+
prompt. Ordinary per-turn failures never dispose.
|
|
96
|
+
- The spawn-grace reaper is now OPT-IN (`spawn({spawnGraceMs})`) and only
|
|
97
|
+
the self-closing catalog-probe client arms it (env
|
|
98
|
+
`HERMES_SPAWN_GRACE_MS`, default 10 min, `0` disables). AGENT-HELD
|
|
99
|
+
clients never arm it: a view-time eager resume may legitimately sit
|
|
100
|
+
unprompted far beyond any grace, and reaping it stamped the terminal
|
|
101
|
+
"hermes gateway client closed" failure that wedged every later prompt
|
|
102
|
+
(the reported wedge). Accepted trade-off until full lazy spawn lands: a
|
|
103
|
+
resumed-but-never-prompted session pins its gateway child until the host
|
|
104
|
+
disposes the agent (session close / owner unload).
|
|
105
|
+
- Turn errors are CLASSIFIED instead of a bare UNKNOWN:
|
|
106
|
+
`GatewayRpcError` 4001/4006 → `NATIVE_SESSION_GONE` (message names the
|
|
107
|
+
stored gateway session), gateway child exit/error → `GATEWAY_CRASH`,
|
|
108
|
+
dead client → `CLIENT_CLOSED`. The code renders in the Web UI error chip
|
|
109
|
+
(`turn/end {kind:"error", error:{message, code}}`).
|
|
110
|
+
- `stored_session_id` DRIFT tolerance: adoption
|
|
111
|
+
(session.create/resume response) fires the client's `onAdopted` listener;
|
|
112
|
+
the provider's map upsert always tracks the latest adopted pair, so a
|
|
113
|
+
gateway that remints the durable key on resume cannot strand future
|
|
114
|
+
resumes on a stale key.
|
|
115
|
+
8. **Model list / switching semantics** (unchanged, recorded): selection
|
|
116
|
+
applies to the session's pinned route immediately (next request); a fresh
|
|
117
|
+
session receives the model at `session.create`. The gateway resolves its
|
|
118
|
+
own provider from the model id; the adapter does not second-guess it.
|
|
119
|
+
9. **Homes** (dev-rules §5/§13): the Hermes runtime keeps its native
|
|
120
|
+
`~/.hermes` home — never redirected, never seeded. `<dshHome>/agents/hermes/`
|
|
121
|
+
holds only the adapter's DSH-side state (`dsh-sessions.json`).
|
|
122
|
+
|
|
123
|
+
## Testing
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
npm run build
|
|
127
|
+
DSH_HOME=<repo>/.tests node --test test/*.test.mjs
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
All unit/fixture tests fake the gateway child (`setGatewayFactory`) — no real
|
|
131
|
+
python child in tests except the opt-in live suite
|
|
132
|
+
(`hermes-client.live.test.mjs`, skips without a live gateway).
|
|
133
|
+
|
|
134
|
+
## Trap: `npm install` materializes the hub link
|
|
135
|
+
|
|
136
|
+
This package depends on `@pgmi-builds/agent-hub` via `file:../agent-hub`. A
|
|
137
|
+
bare `npm install` replaces the symlink with a PHYSICAL copy → a second hub
|
|
138
|
+
instance in the same process → every world webServer stays `pending` forever.
|
|
139
|
+
After any `npm install` here, re-link it:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
rm -rf node_modules/@pgmi-builds/agent-hub
|
|
143
|
+
ln -s ../../../../agent-worlds/agent-hub node_modules/@pgmi-builds/agent-hub
|
|
144
|
+
```
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# @pgmi-builds/agent-adapter-hermes bundle patch (AW-H TUI-gateway line, omp-web form).
|
|
2
|
+
# This file is the `dsh.bundle.patch` layer of the adapter package: when the
|
|
3
|
+
# hermes world profile mounts the bundle, the boot merges THIS patch — mount
|
|
4
|
+
# the Hermes provider plus disable the native components it replaces.
|
|
5
|
+
#
|
|
6
|
+
# Profile-specific values are deliberately NOT here: `webserver.port` (port
|
|
7
|
+
# choice) belongs to the world/profile patch layer (the hermes-standalone
|
|
8
|
+
# profile carries the 127.0.0.1:4985 listener posture).
|
|
9
|
+
|
|
10
|
+
# Mount the Hermes provider: a Cordis Service that registers an AgentFactory
|
|
11
|
+
# on ctx.agents. The provider drives the Hermes Agent TUI gateway (one
|
|
12
|
+
# spawned `python -m tui_gateway.entry` child per session, JSON-RPC) plus
|
|
13
|
+
# resume (dsh-sessions.json mapping, "mapping only"); the model selector is
|
|
14
|
+
# served by a Hermes-backed LlmAdapter publishing every model under the
|
|
15
|
+
# single `hermes` route (gateway model.options, TTL-cached).
|
|
16
|
+
- insert:
|
|
17
|
+
- id: hermes-provider
|
|
18
|
+
name: '@pgmi-builds/agent-adapter-hermes'
|
|
19
|
+
|
|
20
|
+
# Hermes owns the live agent transcript (the gateway's session state); the
|
|
21
|
+
# built-in loop must not double-run alongside the gateway child — the
|
|
22
|
+
# provider bridges turns into the DSH session log itself.
|
|
23
|
+
- id: agent-loop
|
|
24
|
+
disabled: true
|
|
25
|
+
|
|
26
|
+
# The native routes register deepseek-official / pi-ai providers; the Hermes
|
|
27
|
+
# provider registers its own adapter (the single `hermes` route) on ctx.llm.
|
|
28
|
+
# Leaving them mounted would advertise native models alongside Hermes's.
|
|
29
|
+
- id: llm-deepseek
|
|
30
|
+
disabled: true
|
|
31
|
+
- id: llm-pi-ai
|
|
32
|
+
disabled: true
|
|
33
|
+
|
|
34
|
+
# Hermes is single-mode: the gateway drives tools itself, so no Dash preset
|
|
35
|
+
# composition is needed (the provider ships the fixed single-preset roster).
|
|
36
|
+
- id: agent-presets
|
|
37
|
+
disabled: true
|
|
38
|
+
|
|
39
|
+
# The upstream jsonl persistence row STAYS MOUNTED (2026-09-17): the provider
|
|
40
|
+
# writes the DSH session log through it (create/open/append/close), and that
|
|
41
|
+
# log is the WebUI read surface (list/read/replay/workspace grouping are
|
|
42
|
+
# ordinary DSH services over it). Hermes owns the LIVE transcript in its own
|
|
43
|
+
# gateway — the DSH jsonl copy duplicates it by design (session storage
|
|
44
|
+
# duplication accepted, user ruling 2026-09-17).
|
|
45
|
+
|
|
46
|
+
# Hermes approval is RUNTIME: gateway `approval.request` events bridge into
|
|
47
|
+
# Dash `ctx.approval` and answers flow back via `approval.respond` (runtime
|
|
48
|
+
# bridge in src/permission.ts) — there is no launch-only flag to map. The
|
|
49
|
+
# `permission` row below therefore only fixes the DSH approval service's
|
|
50
|
+
# default posture (and its UI presentation); default to danger-full-access.
|
|
51
|
+
# Restate the full 3-preset table: a patch with only defaultPreset silently
|
|
52
|
+
# drops the bundle's 3-preset table to the plugin's built-in 2.
|
|
53
|
+
- id: permission
|
|
54
|
+
config:
|
|
55
|
+
presets:
|
|
56
|
+
read-only:
|
|
57
|
+
sandbox: read-only
|
|
58
|
+
approval: ask
|
|
59
|
+
workspace-write:
|
|
60
|
+
sandbox: workspace-write
|
|
61
|
+
approval: ask
|
|
62
|
+
danger-full-access:
|
|
63
|
+
sandbox: danger-full-access
|
|
64
|
+
approval: never
|
|
65
|
+
defaultPreset: danger-full-access
|
|
66
|
+
|
|
67
|
+
# Directory picker: the stock `directory-picker-auto` is never a simple alias —
|
|
68
|
+
# it RESOLVES the interaction once per boot and mounts BOTH faces through the
|
|
69
|
+
# loader: the host backend package AND the matching CLIENT SURFACE package
|
|
70
|
+
# (`dsh-client-ui-directory-picker-<kind>`). The client surface is what
|
|
71
|
+
# occupies the workspace picker's directory-flow hole; with the hole empty the
|
|
72
|
+
# client hides the "add workspace" entry entirely (WorkspacePicker: `flowAvailable
|
|
73
|
+
# ? [ADD_WORKSPACE] : []`). NEVER just `disabled: true` this row — that removed
|
|
74
|
+
# the button. Pin the in-app browser by mounting both faces of `browse`
|
|
75
|
+
# ourselves (backend first: the surface's browser half drives the capability the
|
|
76
|
+
# backend registers) and unmounting the auto row.
|
|
77
|
+
- insert:
|
|
78
|
+
- id: directory-picker-browse
|
|
79
|
+
name: '@deepseek-ai/dsh-host-directory-picker-browse'
|
|
80
|
+
- id: directory-picker-browse-surface
|
|
81
|
+
name: '@deepseek-ai/dsh-client-ui-directory-picker-browse'
|
|
82
|
+
- id: directory-picker
|
|
83
|
+
disabled: true
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermes-backed `LlmAdapter` registered on `ctx.llm` by the Hermes provider.
|
|
3
|
+
*
|
|
4
|
+
* The browser model selector never touches the Hermes agent directly: it is an
|
|
5
|
+
* RPC round-trip through the apiproxy, which reads `ctx.llm.listProviders /
|
|
6
|
+
* listModels / resolveModel` to build the catalog. This adapter answers those
|
|
7
|
+
* queries from the gateway's live model catalog (`models.ts`: `model.options`
|
|
8
|
+
* RPC, TTL-cached), one route per REAL gateway provider slug (2026-09-17
|
|
9
|
+
* parity ruling) — never the native deepseek-official / pi-ai routes, which
|
|
10
|
+
* the profile disables. The provider boots with the single placeholder route
|
|
11
|
+
* `hermes` and atomically swaps to the distinct slugs once the first catalog
|
|
12
|
+
* probe resolves (`AdapterRegistrationHandle.replace`), so the selector shows
|
|
13
|
+
* real groups (DeepSeek / OpenRouter / …) instead of one ~579-model bucket.
|
|
14
|
+
*
|
|
15
|
+
* The catalog is ASYNC (a gateway RPC), so the adapter is constructed with a
|
|
16
|
+
* `() => Promise<HermesCatalog>` supplier (the provider's memoized
|
|
17
|
+
* probe-client-then-close promise) instead of a home path. `providerInfo` is
|
|
18
|
+
* a synchronous surface, so it serves the last-known gateway row name via
|
|
19
|
+
* the models.ts module cache (capitalized-slug fallback before the first
|
|
20
|
+
* fetch); the async `listModels` / `resolveModel` await the supplier and
|
|
21
|
+
* filter by the entry's own `provider` slug.
|
|
22
|
+
*
|
|
23
|
+
* Model ids stay the VERBATIM gateway selection strings (`session.create
|
|
24
|
+
* {model}` / `/model <id>` consume them as-is); because the r4 dedupe keeps
|
|
25
|
+
* one canonical entry per id, `resolveModel` matches on the (provider, id)
|
|
26
|
+
* pair — a route only ever lists ids that resolve under it.
|
|
27
|
+
*
|
|
28
|
+
* `stream` is never dispatched: Hermes owns generation through its gateway
|
|
29
|
+
* child (`HermesAgent`), so the adapter only serves catalog/metadata queries.
|
|
30
|
+
* It throws rather than fabricating a wire route the harness would misuse.
|
|
31
|
+
*/
|
|
32
|
+
import { LlmAdapter, LlmError, } from "@deepseek-ai/dsh-llm";
|
|
33
|
+
import { hermesProviderDisplayName } from "./models.js";
|
|
34
|
+
/**
|
|
35
|
+
* The boot placeholder route every Hermes model is served under until the
|
|
36
|
+
* first catalog probe resolves and the registration swaps to the real slugs.
|
|
37
|
+
*/
|
|
38
|
+
export const HERMES_PROVIDER_ID = "hermes";
|
|
39
|
+
export class HermesLlmAdapter extends LlmAdapter {
|
|
40
|
+
catalog;
|
|
41
|
+
/**
|
|
42
|
+
* `catalog` = the async catalog supplier — the provider's memoized
|
|
43
|
+
* probe-client-then-close promise, so catalog reads never depend on
|
|
44
|
+
* construction-time context.
|
|
45
|
+
*/
|
|
46
|
+
constructor(catalog) {
|
|
47
|
+
super();
|
|
48
|
+
this.catalog = catalog;
|
|
49
|
+
}
|
|
50
|
+
providerInfo(provider) {
|
|
51
|
+
// Synchronous surface: serves the last-known gateway row name (models.ts
|
|
52
|
+
// module cache, written on every successful fetch); capitalized-slug
|
|
53
|
+
// fallback before the first fetch — the placeholder `hermes` route then
|
|
54
|
+
// renders as "Hermes".
|
|
55
|
+
return { id: provider, name: hermesProviderDisplayName(provider) };
|
|
56
|
+
}
|
|
57
|
+
async listModels(provider) {
|
|
58
|
+
const catalog = await this.catalog();
|
|
59
|
+
return catalog.models
|
|
60
|
+
.filter((model) => model.provider === provider)
|
|
61
|
+
.map((model) => ({
|
|
62
|
+
provider,
|
|
63
|
+
id: model.id,
|
|
64
|
+
name: model.label,
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
async resolveModel(provider, model) {
|
|
68
|
+
const catalog = await this.catalog();
|
|
69
|
+
const found = catalog.models.find((candidate) => candidate.id === model && candidate.provider === provider);
|
|
70
|
+
if (found === undefined) {
|
|
71
|
+
throw new LlmError(`Hermes provider "${provider}" does not serve model "${model}"`, "MODEL_NOT_FOUND");
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
provider,
|
|
75
|
+
id: found.id,
|
|
76
|
+
name: found.label,
|
|
77
|
+
...(found.contextWindow !== undefined && found.contextWindow > 0 ? { context: { contextWindow: found.contextWindow } } : {}),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
stream(_options) {
|
|
81
|
+
throw new LlmError("the Hermes adapter does not stream: Hermes owns generation through its gateway agent", "UNSUPPORTED_STREAM");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
2
|
+
var useValue = arguments.length > 2;
|
|
3
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
4
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
5
|
+
}
|
|
6
|
+
return useValue ? value : void 0;
|
|
7
|
+
};
|
|
8
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
9
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
10
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
11
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
12
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
13
|
+
var _, done = false;
|
|
14
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
15
|
+
var context = {};
|
|
16
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
17
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
18
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
19
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
20
|
+
if (kind === "accessor") {
|
|
21
|
+
if (result === void 0) continue;
|
|
22
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
23
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
24
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
25
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
26
|
+
}
|
|
27
|
+
else if (_ = accept(result)) {
|
|
28
|
+
if (kind === "field") initializers.unshift(_);
|
|
29
|
+
else descriptor[key] = _;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
33
|
+
done = true;
|
|
34
|
+
};
|
|
35
|
+
import { Remote, RemoteError, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
|
|
36
|
+
const HERMES_PRESET = Object.freeze({
|
|
37
|
+
id: "hermes",
|
|
38
|
+
trust: "system",
|
|
39
|
+
path: "",
|
|
40
|
+
name: "Hermes",
|
|
41
|
+
description: "Hermes agent via the agent-hermes adapter",
|
|
42
|
+
});
|
|
43
|
+
const COMPOSITION_TEXT = [
|
|
44
|
+
"# Hermes (fixed roster)",
|
|
45
|
+
"",
|
|
46
|
+
"This deployment runs exactly one agent mode: the Hermes provider bridges Dash",
|
|
47
|
+
"sessions to a native Hermes agent over the TUI gateway JSON-RPC. There is no",
|
|
48
|
+
"per-session plugin composition to configure, so this preset mounts nothing.",
|
|
49
|
+
"",
|
|
50
|
+
"rows: []",
|
|
51
|
+
"",
|
|
52
|
+
].join("\n");
|
|
53
|
+
/** The standing scope key every `hermes` session reads presenters through. */
|
|
54
|
+
const STANDING_KEY = {};
|
|
55
|
+
let SingleHermesPresetRoster = (() => {
|
|
56
|
+
let _classSuper = TypertRemoteService;
|
|
57
|
+
let _instanceExtraInitializers = [];
|
|
58
|
+
let _remoteList_decorators;
|
|
59
|
+
let _remoteRead_decorators;
|
|
60
|
+
let _remoteCopy_decorators;
|
|
61
|
+
let _remoteDelete_decorators;
|
|
62
|
+
let _remoteSelect_decorators;
|
|
63
|
+
return class SingleHermesPresetRoster extends _classSuper {
|
|
64
|
+
static {
|
|
65
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
66
|
+
_remoteList_decorators = [Remote("list")];
|
|
67
|
+
_remoteRead_decorators = [Remote("read")];
|
|
68
|
+
_remoteCopy_decorators = [Remote("copy")];
|
|
69
|
+
_remoteDelete_decorators = [Remote("deletePreset")];
|
|
70
|
+
_remoteSelect_decorators = [Remote("select")];
|
|
71
|
+
__esDecorate(this, null, _remoteList_decorators, { kind: "method", name: "remoteList", static: false, private: false, access: { has: obj => "remoteList" in obj, get: obj => obj.remoteList }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
72
|
+
__esDecorate(this, null, _remoteRead_decorators, { kind: "method", name: "remoteRead", static: false, private: false, access: { has: obj => "remoteRead" in obj, get: obj => obj.remoteRead }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
73
|
+
__esDecorate(this, null, _remoteCopy_decorators, { kind: "method", name: "remoteCopy", static: false, private: false, access: { has: obj => "remoteCopy" in obj, get: obj => obj.remoteCopy }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
74
|
+
__esDecorate(this, null, _remoteDelete_decorators, { kind: "method", name: "remoteDelete", static: false, private: false, access: { has: obj => "remoteDelete" in obj, get: obj => obj.remoteDelete }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
75
|
+
__esDecorate(this, null, _remoteSelect_decorators, { kind: "method", name: "remoteSelect", static: false, private: false, access: { has: obj => "remoteSelect" in obj, get: obj => obj.remoteSelect }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
76
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
77
|
+
}
|
|
78
|
+
constructor(ctx) {
|
|
79
|
+
super(ctx, "agentPresets");
|
|
80
|
+
__runInitializers(this, _instanceExtraInitializers);
|
|
81
|
+
}
|
|
82
|
+
get defaultId() {
|
|
83
|
+
return HERMES_PRESET.id;
|
|
84
|
+
}
|
|
85
|
+
get roots() {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
get authorable() {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
async list() {
|
|
92
|
+
return [HERMES_PRESET];
|
|
93
|
+
}
|
|
94
|
+
async resolve(id) {
|
|
95
|
+
if (id === undefined || id === HERMES_PRESET.id)
|
|
96
|
+
return HERMES_PRESET;
|
|
97
|
+
throw new RemoteError("agent-preset/not-found", `agent-presets: preset "${id}" not found (available: hermes)`, { agentPreset: id, available: [HERMES_PRESET.id] });
|
|
98
|
+
}
|
|
99
|
+
async mount(_agentCtx, id) {
|
|
100
|
+
return this.resolve(id);
|
|
101
|
+
}
|
|
102
|
+
async recompose(_agentCtx, id) {
|
|
103
|
+
return this.resolve(id);
|
|
104
|
+
}
|
|
105
|
+
async read(id) {
|
|
106
|
+
await this.resolve(id);
|
|
107
|
+
return COMPOSITION_TEXT;
|
|
108
|
+
}
|
|
109
|
+
// ── Remote RPC surface (alpha: agentPresets routes come from @Remote) ────
|
|
110
|
+
// The stock dsh-host-apiproxy mapped cordis methods to routes; alpha serves
|
|
111
|
+
// them from @Remote decorators on a TypertRemoteService. A plain Service
|
|
112
|
+
// registers no routes, which is why Settings → Agent Preset 404'd.
|
|
113
|
+
async remoteList() {
|
|
114
|
+
if (process.env.HERMES_TRACE === "1")
|
|
115
|
+
process.stderr.write("[hermes-roster] remoteList INVOKED\n");
|
|
116
|
+
return {
|
|
117
|
+
presets: [
|
|
118
|
+
{
|
|
119
|
+
id: HERMES_PRESET.id,
|
|
120
|
+
trust: HERMES_PRESET.trust,
|
|
121
|
+
isDefault: true,
|
|
122
|
+
name: HERMES_PRESET.name,
|
|
123
|
+
description: HERMES_PRESET.description,
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
authorable: this.authorable,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
async remoteRead(agentPreset) {
|
|
130
|
+
const preset = await this.resolve(agentPreset);
|
|
131
|
+
return {
|
|
132
|
+
agentPreset: preset.id,
|
|
133
|
+
trust: preset.trust,
|
|
134
|
+
content: await this.read(preset.id),
|
|
135
|
+
name: preset.name,
|
|
136
|
+
description: preset.description,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
async remoteCopy(from, id, name) {
|
|
140
|
+
await this.copy(from, id, name);
|
|
141
|
+
}
|
|
142
|
+
async remoteDelete(id) {
|
|
143
|
+
await this.remove(id);
|
|
144
|
+
}
|
|
145
|
+
/** Fixed roster: one preset, so selecting it is a no-op ack; anything else is unknown. */
|
|
146
|
+
async remoteSelect(agent, agentPreset) {
|
|
147
|
+
const preset = await this.resolve(agentPreset);
|
|
148
|
+
return preset.id;
|
|
149
|
+
}
|
|
150
|
+
async copy(_from, id, _name) {
|
|
151
|
+
throw new RemoteError("agent-preset/read-only", `agent-presets: preset "${id}" cannot be written: the Hermes roster is fixed and ships exactly one preset`, { agentPreset: id, reason: "the Hermes roster is fixed and ships exactly one preset" });
|
|
152
|
+
}
|
|
153
|
+
async remove(id) {
|
|
154
|
+
throw new RemoteError("agent-preset/read-only", `agent-presets: preset "${id}" cannot be written: the Hermes roster is fixed and ships exactly one preset`, { agentPreset: id, reason: "the Hermes roster is fixed and ships exactly one preset" });
|
|
155
|
+
}
|
|
156
|
+
serviceFor(_agent, _name) {
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
composeFrom(_agentCtx, _parentCtx) {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
composedPreset(_agentCtx) {
|
|
163
|
+
return HERMES_PRESET.id;
|
|
164
|
+
}
|
|
165
|
+
async standingKeyFor(id) {
|
|
166
|
+
await this.resolve(id);
|
|
167
|
+
return STANDING_KEY;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
})();
|
|
171
|
+
export { SingleHermesPresetRoster };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `agentPreset` session projection, owned by the adapter.
|
|
3
|
+
*
|
|
4
|
+
* Upstream registers this fold from the `dsh-agent-presets` package, which our
|
|
5
|
+
* patch disables (the Hermes provider replaces preset composition wholesale).
|
|
6
|
+
* But the Web UI depends on the projection regardless: the preset hero chip
|
|
7
|
+
* renders only when `session.projectionValues.agentPreset` resolves
|
|
8
|
+
* (`presetOf` in ui-agent-preset/seat-store.ts), and the session-header label
|
|
9
|
+
* reads it too. Without a registrant the projection is absent for every
|
|
10
|
+
* adapter session and both surfaces stay blank — roster or no roster.
|
|
11
|
+
*
|
|
12
|
+
* Same fold as upstream (packages/preset/agent-presets/src/session.ts):
|
|
13
|
+
* initialized from the creation header, advanced by `agent-preset/selected`
|
|
14
|
+
* events, which this adapter synthesizes (`"hermes"`) on every session it
|
|
15
|
+
* prepares. Registered under the provider's own fiber via
|
|
16
|
+
* `ctx.inject(['sessionProjections'])`, so the key lives exactly as long as
|
|
17
|
+
* the provider is mounted.
|
|
18
|
+
*/
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
const agentPresetSchema = z.union([z.string(), z.null()]);
|
|
21
|
+
/** Current Session preset, initialized from its header and advanced by selection events. */
|
|
22
|
+
export const hermesAgentPresetProjection = {
|
|
23
|
+
key: "agentPreset",
|
|
24
|
+
stateSchema: agentPresetSchema,
|
|
25
|
+
// adapter-hermes is single-preset: default every session to "hermes" so the
|
|
26
|
+
// header chip resolves for scan-native (never-resumed) sessions too,
|
|
27
|
+
// instead of only those whose log carries an `agent-preset/selected` event.
|
|
28
|
+
init: (header) => header.agentPreset ?? "hermes",
|
|
29
|
+
apply: (state, event) => event.type === "agent-preset/selected"
|
|
30
|
+
? event.data.agentPreset
|
|
31
|
+
: state,
|
|
32
|
+
wire: { viewSchema: agentPresetSchema, view: (state) => state },
|
|
33
|
+
stateVersion: 1,
|
|
34
|
+
};
|