super-dsh 0.1.10 → 0.1.11

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.
Files changed (50) hide show
  1. package/README.md +18 -2
  2. package/agent-agy/dist/agent.js +19 -1
  3. package/agent-claude/dist/adapter.js +106 -31
  4. package/agent-claude/dist/agent.js +177 -22
  5. package/agent-claude/dist/claude-client.js +84 -43
  6. package/agent-claude/dist/claude-launch.js +101 -0
  7. package/agent-claude/dist/claude-native-config.js +169 -0
  8. package/agent-claude/dist/claude-provider-gateway.js +340 -0
  9. package/agent-claude/dist/claude-providers.js +391 -0
  10. package/agent-claude/dist/claude-route.js +152 -0
  11. package/agent-claude/dist/index.js +214 -37
  12. package/agent-claude/dist/models.js +14 -0
  13. package/agent-claude/dist/sdk-runtime.js +320 -0
  14. package/agent-claude/package.json +6 -4
  15. package/agent-codex/README.md +18 -6
  16. package/agent-codex/dist/adapter.js +3 -2
  17. package/agent-codex/dist/agent.js +41 -7
  18. package/agent-codex/dist/app-server-client.js +7 -1
  19. package/agent-codex/dist/app-server-wire.js +10 -22
  20. package/agent-codex/dist/codex-launch.js +10 -32
  21. package/agent-codex/dist/codex-native-config.js +9 -3
  22. package/agent-codex/dist/codex-oauth.js +153 -0
  23. package/agent-codex/dist/codex-provider-gateway.js +36 -38
  24. package/agent-codex/dist/codex-providers.js +20 -7
  25. package/agent-codex/dist/codex-runtime.js +120 -0
  26. package/agent-codex/dist/index.js +18 -11
  27. package/agent-codex/package.json +1 -1
  28. package/agent-codex-sdk/dist/adapter.js +3 -2
  29. package/agent-codex-sdk/dist/agent.js +35 -1
  30. package/agent-codex-sdk/dist/codex-native-config.js +9 -3
  31. package/agent-codex-sdk/dist/codex-oauth.js +153 -0
  32. package/agent-codex-sdk/dist/codex-provider-gateway.js +36 -38
  33. package/agent-codex-sdk/dist/codex-providers.js +20 -7
  34. package/agent-codex-sdk/dist/codex-sdk-launch.js +5 -38
  35. package/agent-codex-sdk/dist/index.js +18 -11
  36. package/agent-codex-sdk/dist/sdk-runtime.js +2 -2
  37. package/agent-hermes/dist/agent.js +46 -2
  38. package/agent-hermes/dist/hermes-client.js +49 -1
  39. package/agent-omp/dist/agent.js +17 -4
  40. package/agent-omp-sdk/dist/agent.d.ts +11 -0
  41. package/agent-omp-sdk/dist/agent.js +17 -4
  42. package/agent-pi/dist/agent.js +33 -1
  43. package/client/index.js +150 -58
  44. package/package.json +2 -3
  45. package/superd-hub/dist/client/agent-settings.d.ts +66 -12
  46. package/superd-hub/dist/client/agent-settings.js +85 -14
  47. package/superd-hub/dist/client/runtime-settings-api.js +5 -5
  48. package/superd-hub/dist/world-mount.d.ts +6 -5
  49. package/superd-hub/dist/world-mount.js +6 -6
  50. package/superd-hub/lib/client/index.js +150 -58
@@ -0,0 +1,340 @@
1
+ /**
2
+ * claude-provider-gateway.ts — the adapter-owned Remote behind the Hub's Claude
3
+ * settings panel (2026-10-04 multi-provider ruling).
4
+ *
5
+ * Why a Remote and not a hub route: the provider list is ADAPTER config
6
+ * (dev-rules §12), so the adapter owns it and the hub only forwards.
7
+ * `TypertRemoteService` + `@Remote` is the whole mechanism — standard (TC39)
8
+ * method decorators recorded on the class prototype, read back at dispatch time
9
+ * by the gateway's source-claim scan. No codegen, no compiler-injected metadata,
10
+ * which is why a plain `tsc` adapter build can ship one.
11
+ *
12
+ * Wire contract (`superd-hub/src/client/runtime-settings-api.ts` builds each
13
+ * envelope; the hub carrier forwards `envelope.method` verbatim):
14
+ *
15
+ * request POST /hub/api?agent=claude
16
+ * { type:'client-request', rpcId, method:'claudeProviders/<op>',
17
+ * payload: { args: { request?: … } } }
18
+ * response { type:'server-response', rpcId, result:{ ok:true, value:<view> } }
19
+ *
20
+ * There is no `select`: which provider a SESSION runs on is DSH's own
21
+ * per-session `model/selection`, chosen from the harness's model selector. This
22
+ * panel is a list editor — add, edit, remove — plus the reconcile with the
23
+ * user's `settings.json` that {@link syncFromNative} performs.
24
+ *
25
+ * The class is a delegation shell on purpose: every operation is a plain
26
+ * function over (worldHome, claudeHome), so the entire contract — the key
27
+ * privacy rule, the validation, the sync, the view projection — is pinnable
28
+ * under `node --test` with no Cordis Context and no real home.
29
+ *
30
+ * @module agent-claude/claude-provider-gateway
31
+ */
32
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
33
+ var useValue = arguments.length > 2;
34
+ for (var i = 0; i < initializers.length; i++) {
35
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
36
+ }
37
+ return useValue ? value : void 0;
38
+ };
39
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
40
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
41
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
42
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
43
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
44
+ var _, done = false;
45
+ for (var i = decorators.length - 1; i >= 0; i--) {
46
+ var context = {};
47
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
48
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
49
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
50
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
51
+ if (kind === "accessor") {
52
+ if (result === void 0) continue;
53
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
54
+ if (_ = accept(result.get)) descriptor.get = _;
55
+ if (_ = accept(result.set)) descriptor.set = _;
56
+ if (_ = accept(result.init)) initializers.unshift(_);
57
+ }
58
+ else if (_ = accept(result)) {
59
+ if (kind === "field") initializers.unshift(_);
60
+ else descriptor[key] = _;
61
+ }
62
+ }
63
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
64
+ done = true;
65
+ };
66
+ import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
67
+ import { isProviderId, providerStorePath, readProviderStore, removeProvider, syncNativeProviders, upsertProvider, writeProviderStore, } from "./claude-providers.js";
68
+ import { readNativeModel, readNativeProviders, } from "./claude-native-config.js";
69
+ import { join } from "node:path";
70
+ function isRecord(value) {
71
+ return value !== null && typeof value === "object" && !Array.isArray(value);
72
+ }
73
+ function nonEmpty(value) {
74
+ return typeof value === "string" && value.trim() !== "" ? value : undefined;
75
+ }
76
+ /**
77
+ * Whether an entry's key is actually available right now.
78
+ *
79
+ * Claude has no auth-manager shadowing to work around (the codex face's
80
+ * ChatGPT-login wrinkle has no analogue here): both kinds carry the literal
81
+ * secret and reach the child through its own environment
82
+ * (`claude-launch.ts`), so the store IS the whole truth — an empty value is the
83
+ * real "keyed outside Super D" state and nothing else can supply the key.
84
+ */
85
+ function keyAvailable(entry) {
86
+ return entry.auth.value !== "";
87
+ }
88
+ function project(entry) {
89
+ return {
90
+ id: entry.id,
91
+ label: entry.label,
92
+ baseUrl: entry.baseUrl,
93
+ models: entry.models,
94
+ authKind: entry.auth.kind,
95
+ hasKey: keyAvailable(entry),
96
+ };
97
+ }
98
+ /** Turn one parsed native table into the store's import shape. */
99
+ function toImport(table) {
100
+ // The native read emits a table only when a usable endpoint exists, and it
101
+ // always classifies an auth shape by then; the optionals are the TYPE being
102
+ // honest about a hand-edited file, so the fold keeps only tables that
103
+ // actually carry the pair.
104
+ if (table.baseUrl === undefined || table.auth === undefined)
105
+ return undefined;
106
+ return {
107
+ baseUrl: table.baseUrl,
108
+ auth: table.auth,
109
+ models: table.models,
110
+ ...(table.aliases === undefined ? {} : { aliases: table.aliases }),
111
+ };
112
+ }
113
+ /**
114
+ * Reconcile the canonical store against the user's `settings.json` env.
115
+ *
116
+ * This is the sync the 2026-10-04 ruling asks for: the store is canonical, the
117
+ * native settings env is a SOURCE, and the two are joined by endpoint URL (the
118
+ * only identity a hand-written settings env reliably carries — it names no
119
+ * provider). It runs at adapter startup and every ten minutes thereafter, and
120
+ * it is idempotent and silent — the store is only rewritten when something
121
+ * actually moved.
122
+ *
123
+ * @param worldHome - the world's DSH home.
124
+ * @param claudeHome - the native claude home (read only).
125
+ * @returns the effective store plus what the sync changed.
126
+ */
127
+ export function syncFromNative(worldHome, claudeHome) {
128
+ const store = readProviderStore(worldHome);
129
+ const imported = readNativeProviders(claudeHome)
130
+ .map(toImport)
131
+ .filter((value) => value !== undefined);
132
+ const result = syncNativeProviders(store, imported);
133
+ if (result.added.length > 0 || result.updated.length > 0) {
134
+ writeProviderStore(worldHome, result.store);
135
+ }
136
+ return result;
137
+ }
138
+ /** The view for one (already loaded) store. */
139
+ function viewOf(worldHome, claudeHome, store, syncedAt, note) {
140
+ // A settings env declares at most one endpoint, so the FIRST table is the
141
+ // one in force. `readNativeModel` is read on its own because a native setup
142
+ // on the default Anthropic endpoint carries a model but no base URL.
143
+ const baseUrl = readNativeProviders(claudeHome)[0]?.baseUrl;
144
+ const model = readNativeModel(claudeHome);
145
+ const native = baseUrl === undefined && model === undefined
146
+ ? null
147
+ : {
148
+ ...(baseUrl === undefined ? {} : { baseUrl }),
149
+ ...(model === undefined ? {} : { model }),
150
+ };
151
+ return {
152
+ providers: store.providers.map(project),
153
+ native,
154
+ paths: {
155
+ store: providerStorePath(worldHome),
156
+ settings: join(claudeHome, "settings.json"),
157
+ },
158
+ ...(syncedAt === undefined ? {} : { syncedAt }),
159
+ ...(note === undefined ? {} : { note }),
160
+ };
161
+ }
162
+ /**
163
+ * Read the panel state, reconciling with the native `settings.json` env first.
164
+ *
165
+ * Reading never writes the native home; the only write this can perform is to
166
+ * the adapter's own store, and only when the sync found something new.
167
+ *
168
+ * @param worldHome - the world's DSH home.
169
+ * @param claudeHome - the native claude home.
170
+ * @param syncedAt - epoch ms to report as the last reconcile.
171
+ * @returns the panel's state.
172
+ */
173
+ export function readProvidersView(worldHome, claudeHome, syncedAt) {
174
+ const { store, added, updated } = syncFromNative(worldHome, claudeHome);
175
+ const note = added.length === 0 && updated.length === 0
176
+ ? undefined
177
+ : `Imported ${added.length} provider(s) from your Claude settings.json${updated.length === 0 ? "" : ` and refreshed ${updated.join(", ")}`}.`;
178
+ // A read IS the reconcile, so the timestamp is the read's own unless the
179
+ // caller has a better one — otherwise the panel would never have one to show.
180
+ return viewOf(worldHome, claudeHome, store, syncedAt ?? Date.now(), note);
181
+ }
182
+ /**
183
+ * Validate one `claudeProviders/save` request.
184
+ *
185
+ * @param request - the wire payload's `request` argument.
186
+ * @param existing - the entry this save replaces, so an edit that does not
187
+ * re-type the key keeps the stored one.
188
+ * @throws {TypeError} naming the offending field.
189
+ */
190
+ export function normalizeSaveRequest(request, existing) {
191
+ if (!isRecord(request) || !isRecord(request["provider"])) {
192
+ throw new TypeError("claudeProviders/save: provider must be an object");
193
+ }
194
+ const raw = request["provider"];
195
+ const id = raw["id"];
196
+ if (!isProviderId(id)) {
197
+ throw new TypeError(`claudeProviders/save: id must match /^[a-z0-9][a-z0-9-]*$/ (got ${JSON.stringify(id)})`);
198
+ }
199
+ const label = nonEmpty(raw["label"]);
200
+ if (label === undefined)
201
+ throw new TypeError("claudeProviders/save: label must be a non-empty string");
202
+ const baseUrl = nonEmpty(raw["baseUrl"]);
203
+ if (baseUrl === undefined)
204
+ throw new TypeError("claudeProviders/save: baseUrl must be a non-empty string");
205
+ let parsed;
206
+ try {
207
+ parsed = new URL(baseUrl);
208
+ }
209
+ catch {
210
+ throw new TypeError(`claudeProviders/save: baseUrl is not a URL (got ${JSON.stringify(baseUrl)})`);
211
+ }
212
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
213
+ throw new TypeError(`claudeProviders/save: baseUrl must be http(s) (got ${parsed.protocol})`);
214
+ }
215
+ const rawModels = raw["models"];
216
+ // An empty list is legal: it is the state a provider folded in from a native
217
+ // settings env that names no alias model arrives in, and the panel must be
218
+ // able to save an edit without inventing models.
219
+ if (!Array.isArray(rawModels)) {
220
+ throw new TypeError("claudeProviders/save: models must be an array");
221
+ }
222
+ const models = rawModels.map((value) => {
223
+ if (!isRecord(value))
224
+ throw new TypeError("claudeProviders/save: every entry of models must be an object");
225
+ const modelId = nonEmpty(value["id"]);
226
+ if (modelId === undefined)
227
+ throw new TypeError("claudeProviders/save: every entry of models needs a non-empty id");
228
+ const displayName = nonEmpty(value["displayName"]);
229
+ const contextWindow = value["contextWindow"];
230
+ return {
231
+ id: modelId,
232
+ ...(displayName === undefined ? {} : { displayName }),
233
+ ...(typeof contextWindow === "number" && Number.isSafeInteger(contextWindow) && contextWindow > 0
234
+ ? { contextWindow }
235
+ : {}),
236
+ };
237
+ });
238
+ // `aliases` is deliberately NOT a save field: the DEFAULT_*_MODEL mapping is
239
+ // derived by the native fold (spec §2.2), the panel never edits it, and a
240
+ // save of a folded entry drops it — the next reconcile re-derives it from
241
+ // the live settings env for as long as the endpoint still matches.
242
+ const rawAuth = raw["auth"];
243
+ if (!isRecord(rawAuth))
244
+ throw new TypeError("claudeProviders/save: auth must be an object");
245
+ const kind = rawAuth["kind"];
246
+ if (kind !== "authToken" && kind !== "apiKey") {
247
+ throw new TypeError(`claudeProviders/save: auth.kind must be "authToken" or "apiKey" (got ${JSON.stringify(kind)})`);
248
+ }
249
+ const value = rawAuth["value"];
250
+ if (typeof value !== "string")
251
+ throw new TypeError("claudeProviders/save: auth.value must be a string");
252
+ // An empty value on an edit means "keep the stored one" — the panel is
253
+ // write-only for keys, so it cannot echo the existing value back. The keep
254
+ // is per kind: a secret of the other shape is not this provider's key.
255
+ const kept = value === "" && existing?.auth.kind === kind ? existing.auth.value : value;
256
+ const auth = { kind, value: kept };
257
+ return { id, label, baseUrl, auth, models, origin: "user" };
258
+ }
259
+ /**
260
+ * Insert or replace one provider.
261
+ *
262
+ * Nothing native is written, and nothing is "activated": the entry sits in the
263
+ * list, and the next session that picks one of its models gets a child pinned
264
+ * to it — `claude-launch.ts` builds the provider's environment at spawn.
265
+ */
266
+ export function saveProvider(worldHome, claudeHome, request, syncedAt) {
267
+ const store = readProviderStore(worldHome);
268
+ const id = isRecord(request) && isRecord(request["provider"]) ? request["provider"]["id"] : undefined;
269
+ const existing = store.providers.find((entry) => entry.id === id);
270
+ const entry = normalizeSaveRequest(request, existing);
271
+ const next = upsertProvider(store, entry);
272
+ writeProviderStore(worldHome, next);
273
+ return viewOf(worldHome, claudeHome, next, syncedAt);
274
+ }
275
+ /** Drop one provider. The native settings are left alone — removing from this list is not unconfiguring Claude Code. */
276
+ export function removeProviderEntry(worldHome, claudeHome, request, syncedAt) {
277
+ const id = isRecord(request) ? nonEmpty(request["id"]) : undefined;
278
+ if (id === undefined)
279
+ throw new TypeError("claudeProviders/remove: id must be a non-empty string");
280
+ const store = readProviderStore(worldHome);
281
+ const next = removeProvider(store, id);
282
+ writeProviderStore(worldHome, next);
283
+ return viewOf(worldHome, claudeHome, next, syncedAt);
284
+ }
285
+ /**
286
+ * The adapter's provider Remote. One instance per world context.
287
+ */
288
+ let ClaudeProvidersGateway = (() => {
289
+ let _classSuper = TypertRemoteService;
290
+ let _instanceExtraInitializers = [];
291
+ let _get_decorators;
292
+ let _save_decorators;
293
+ let _remove_decorators;
294
+ return class ClaudeProvidersGateway extends _classSuper {
295
+ static {
296
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
297
+ _get_decorators = [Remote("get")];
298
+ _save_decorators = [Remote("save")];
299
+ _remove_decorators = [Remote("remove")];
300
+ __esDecorate(this, null, _get_decorators, { kind: "method", name: "get", static: false, private: false, access: { has: obj => "get" in obj, get: obj => obj.get }, metadata: _metadata }, null, _instanceExtraInitializers);
301
+ __esDecorate(this, null, _save_decorators, { kind: "method", name: "save", static: false, private: false, access: { has: obj => "save" in obj, get: obj => obj.save }, metadata: _metadata }, null, _instanceExtraInitializers);
302
+ __esDecorate(this, null, _remove_decorators, { kind: "method", name: "remove", static: false, private: false, access: { has: obj => "remove" in obj, get: obj => obj.remove }, metadata: _metadata }, null, _instanceExtraInitializers);
303
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
304
+ }
305
+ worldHome = __runInitializers(this, _instanceExtraInitializers);
306
+ claudeHome;
307
+ onChange;
308
+ /**
309
+ * @param ctx - the world context this service registers on (namespace `claudeProviders`).
310
+ * @param worldHome - the world's DSH home holding adapter state (`<dshHome>/agents/claude`).
311
+ * @param claudeHome - the USER's real claude home (native settings live there), resolved lazily so construction stays zero-IO.
312
+ * @param onChange - notified after any mutation, so the provider's `ctx.llm` routes can be re-registered.
313
+ */
314
+ constructor(ctx, worldHome, claudeHome, onChange = () => { }) {
315
+ super(ctx, "claudeProviders");
316
+ this.worldHome = worldHome;
317
+ this.claudeHome = claudeHome;
318
+ this.onChange = onChange;
319
+ }
320
+ /** Read the panel state, reconciling the store with the native settings env first. Never writes the native home. */
321
+ async get() {
322
+ const view = readProvidersView(this.worldHome, this.claudeHome());
323
+ this.onChange();
324
+ return view;
325
+ }
326
+ /** Add or edit one provider. */
327
+ async save(request) {
328
+ const view = saveProvider(this.worldHome, this.claudeHome(), request);
329
+ this.onChange();
330
+ return view;
331
+ }
332
+ /** Drop one provider from the list. */
333
+ async remove(request) {
334
+ const view = removeProviderEntry(this.worldHome, this.claudeHome(), request);
335
+ this.onChange();
336
+ return view;
337
+ }
338
+ };
339
+ })();
340
+ export { ClaudeProvidersGateway };