@khorsheed/dsh-local-agent-dsh 0.2.0-rc.1 → 0.2.0
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/CHANGELOG.md +16 -0
- package/README.en.md +23 -136
- package/README.i18n.yaml +2 -2
- package/README.md +23 -136
- package/lib/client.js +155 -83
- package/lib/{dsh-cli-provider-UhWvivte.js → dsh-cli-provider-B7s3zH0m.js} +108 -27
- package/lib/dsh-cli-provider-DYRuRpVz.js +1920 -0
- package/lib/dsh-cli-provider.js +1 -1
- package/lib/index.js +40 -27
- package/lib/provision.js +327 -152
- package/lib/types/client/SettingsCard.d.ts +41 -13
- package/lib/types/client/SettingsCard.js +22 -6
- package/lib/types/client/icons.d.ts +1 -1
- package/lib/types/client/index.d.ts +5 -27
- package/lib/types/client/index.js +87 -44
- package/lib/types/client/locales.js +2 -2
- package/lib/types/dsh-cli-provider.js +82 -47
- package/lib/types/index.d.ts +9 -1
- package/lib/types/index.js +35 -21
- package/lib/types/live-driver.js +72 -54
- package/lib/types/model-catalog.d.ts +1 -0
- package/lib/types/model-catalog.js +9 -4
- package/lib/types/model-launch.d.ts +14 -0
- package/lib/types/model-launch.js +58 -0
- package/lib/types/provision.d.ts +166 -119
- package/lib/types/provision.js +376 -166
- package/lib/types/session-mirror.d.ts +55 -0
- package/lib/types/session-mirror.js +155 -1
- package/locale/en.json +2 -2
- package/locale/zh.json +2 -2
- package/package.json +27 -25
- package/lib/invariant.js +0 -33
- package/lib/types/invariant.d.ts +0 -16
- package/lib/types/invariant.js +0 -37
package/lib/client.js
CHANGED
|
@@ -100,7 +100,7 @@ window.__ModuleLoader__.load({
|
|
|
100
100
|
const NS = "local-agent-dsh";
|
|
101
101
|
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
102
102
|
const zh = {
|
|
103
|
-
"card.title": "
|
|
103
|
+
"card.title": "本机 DSH 委派",
|
|
104
104
|
"card.description": "dsh 自委派:开关、认证状态与常驻模式(live)",
|
|
105
105
|
"card.expand": "展开",
|
|
106
106
|
"card.collapse": "收起",
|
|
@@ -132,7 +132,7 @@ window.__ModuleLoader__.load({
|
|
|
132
132
|
};
|
|
133
133
|
/** English dictionary, checked complete against the zh key set. */
|
|
134
134
|
const en = {
|
|
135
|
-
"card.title": "Local
|
|
135
|
+
"card.title": "Local DSH Delegation",
|
|
136
136
|
"card.description": "dsh self-delegation: toggle, credential status, and resident (live) mode",
|
|
137
137
|
"card.expand": "Expand",
|
|
138
138
|
"card.collapse": "Collapse",
|
|
@@ -219,6 +219,14 @@ window.__ModuleLoader__.load({
|
|
|
219
219
|
};
|
|
220
220
|
}
|
|
221
221
|
/**
|
|
222
|
+
* Subscribe one harness's published status (no probe of its own): a sibling
|
|
223
|
+
* surface that WRITES the credential (the endpoint form's save) publishes
|
|
224
|
+
* through the same bus, and every block showing the status flips at once.
|
|
225
|
+
*/
|
|
226
|
+
function usePublishedAuthStatus(harnessId) {
|
|
227
|
+
return (0, react.useSyncExternalStore)(subscribe, () => statuses.get(harnessId));
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
222
230
|
* Read one harness's latest known auth status. Every mount probes once and
|
|
223
231
|
* publishes — a published value only snapshots the past (a credential expiring
|
|
224
232
|
* mid-session must flip the dot at the next view, not stay green forever);
|
|
@@ -255,30 +263,30 @@ window.__ModuleLoader__.load({
|
|
|
255
263
|
document.head.appendChild(tag);
|
|
256
264
|
}
|
|
257
265
|
var ProviderAuthBlock_module_css_default = {
|
|
266
|
+
"rowCard": "On5p7W_rowCard",
|
|
267
|
+
"rowName": "On5p7W_rowName",
|
|
258
268
|
"rowHead": "On5p7W_rowHead",
|
|
259
|
-
"rows": "On5p7W_rows",
|
|
260
|
-
"rowTag": "On5p7W_rowTag",
|
|
261
|
-
"status": "On5p7W_status",
|
|
262
|
-
"intro": "On5p7W_intro",
|
|
263
269
|
"section": "On5p7W_section",
|
|
264
|
-
"
|
|
270
|
+
"dotOff": "On5p7W_dotOff",
|
|
271
|
+
"logoutButton": "On5p7W_logoutButton",
|
|
272
|
+
"rosterError": "On5p7W_rosterError",
|
|
273
|
+
"dotOn": "On5p7W_dotOn",
|
|
274
|
+
"promptText": "On5p7W_promptText",
|
|
265
275
|
"title": "On5p7W_title",
|
|
276
|
+
"rowTag": "On5p7W_rowTag",
|
|
277
|
+
"intro": "On5p7W_intro",
|
|
278
|
+
"rows": "On5p7W_rows",
|
|
266
279
|
"dotPending": "On5p7W_dotPending",
|
|
267
|
-
"rosterError": "On5p7W_rosterError",
|
|
268
|
-
"retryButton": "On5p7W_retryButton",
|
|
269
|
-
"loginPrompt": "On5p7W_loginPrompt",
|
|
270
|
-
"codeInput": "On5p7W_codeInput",
|
|
271
|
-
"logoutButton": "On5p7W_logoutButton",
|
|
272
|
-
"loginButton": "On5p7W_loginButton",
|
|
273
280
|
"rowActions": "On5p7W_rowActions",
|
|
274
|
-
"
|
|
281
|
+
"loginButton": "On5p7W_loginButton",
|
|
275
282
|
"rowPending": "On5p7W_rowPending",
|
|
283
|
+
"retryButton": "On5p7W_retryButton",
|
|
276
284
|
"openPage": "On5p7W_openPage",
|
|
277
|
-
"
|
|
278
|
-
"dotOn": "On5p7W_dotOn",
|
|
285
|
+
"status": "On5p7W_status",
|
|
279
286
|
"credentialDot": "On5p7W_credentialDot",
|
|
280
|
-
"
|
|
281
|
-
"rowIdentity": "On5p7W_rowIdentity"
|
|
287
|
+
"loginPrompt": "On5p7W_loginPrompt",
|
|
288
|
+
"rowIdentity": "On5p7W_rowIdentity",
|
|
289
|
+
"codeInput": "On5p7W_codeInput"
|
|
282
290
|
};
|
|
283
291
|
//#endregion
|
|
284
292
|
//#region ../local-agent/src/client/ProviderAuthBlock.tsx
|
|
@@ -312,12 +320,13 @@ window.__ModuleLoader__.load({
|
|
|
312
320
|
* for the retainedBy.mainView count on each summary (the label is ui-session's
|
|
313
321
|
* declaration merge, read here as a plain duck-typed count so this package
|
|
314
322
|
* needs no ui-session edge), with the legacy field as the older-host fallback.
|
|
323
|
+
* Exported for sibling cards that run slash commands from their own rows.
|
|
315
324
|
*/
|
|
316
325
|
const mainSessionId = (list) => Object.values(list.byId ?? {}).find((session) => (session.retainedBy?.mainView ?? 0) > 0)?.id ?? list.current;
|
|
317
326
|
/**
|
|
318
327
|
* One harness's auth interactions, shared between the per-provider settings
|
|
319
|
-
* cards (
|
|
320
|
-
*
|
|
328
|
+
* cards (each a `plugins.item` entry on the Plugins page, registered while
|
|
329
|
+
* the Host serves the provider's namespace): auth status through
|
|
321
330
|
* the read-only Remote channel and a web-login action that runs
|
|
322
331
|
* `/<harness> login`, surfaces the device-code prompt, and offers a link to
|
|
323
332
|
* the authorization page. The login command returns at prompt time while the
|
|
@@ -350,7 +359,8 @@ window.__ModuleLoader__.load({
|
|
|
350
359
|
const capabilities = probe === void 0 ? {} : {
|
|
351
360
|
loginable: probe.loginable ?? true,
|
|
352
361
|
logoutable: probe.logoutable ?? true,
|
|
353
|
-
...probe.loginAwaitingCode === true ? { loginAwaitingCode: true } : {}
|
|
362
|
+
...probe.loginAwaitingCode === true ? { loginAwaitingCode: true } : {},
|
|
363
|
+
...probe.effectiveSettings?.cliVersion === void 0 ? {} : { cliVersion: probe.effectiveSettings.cliVersion }
|
|
354
364
|
};
|
|
355
365
|
setView((prev) => statusKind === "authenticated" ? {
|
|
356
366
|
status: statusKind,
|
|
@@ -426,7 +436,7 @@ window.__ModuleLoader__.load({
|
|
|
426
436
|
setView({ status: "unavailable" });
|
|
427
437
|
});
|
|
428
438
|
};
|
|
429
|
-
const statusKind = view.status;
|
|
439
|
+
const statusKind = usePublishedAuthStatus(harness.id) ?? view.status;
|
|
430
440
|
const authenticated = statusKind === "authenticated";
|
|
431
441
|
const loginPending = pendingSince !== void 0;
|
|
432
442
|
const loginable = view.loginable ?? true;
|
|
@@ -450,6 +460,10 @@ window.__ModuleLoader__.load({
|
|
|
450
460
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
451
461
|
className: ProviderAuthBlock_module_css_default.rowTag,
|
|
452
462
|
children: harness.id
|
|
463
|
+
}),
|
|
464
|
+
view.cliVersion !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
465
|
+
className: ProviderAuthBlock_module_css_default.rowTag,
|
|
466
|
+
children: view.cliVersion
|
|
453
467
|
})
|
|
454
468
|
]
|
|
455
469
|
}),
|
|
@@ -551,9 +565,9 @@ window.__ModuleLoader__.load({
|
|
|
551
565
|
document.head.appendChild(tag);
|
|
552
566
|
}
|
|
553
567
|
var AuthStatusDot_module_css_default = {
|
|
554
|
-
"dotOn": "OFjNUG_dotOn",
|
|
555
568
|
"credentialDot": "OFjNUG_credentialDot",
|
|
556
569
|
"dotOff": "OFjNUG_dotOff",
|
|
570
|
+
"dotOn": "OFjNUG_dotOn",
|
|
557
571
|
"dotPending": "OFjNUG_dotPending"
|
|
558
572
|
};
|
|
559
573
|
//#endregion
|
|
@@ -580,40 +594,40 @@ window.__ModuleLoader__.load({
|
|
|
580
594
|
document.head.appendChild(tag);
|
|
581
595
|
}
|
|
582
596
|
var SettingsCard_module_css_default = {
|
|
583
|
-
"
|
|
584
|
-
"
|
|
585
|
-
"
|
|
586
|
-
"
|
|
587
|
-
"
|
|
597
|
+
"errorText": "_3LBPpW_errorText",
|
|
598
|
+
"modelMenu": "_3LBPpW_modelMenu",
|
|
599
|
+
"rowLabel": "_3LBPpW_rowLabel",
|
|
600
|
+
"description": "_3LBPpW_description",
|
|
601
|
+
"body": "_3LBPpW_body",
|
|
588
602
|
"chevron": "_3LBPpW_chevron",
|
|
603
|
+
"modelMenuButton": "_3LBPpW_modelMenuButton",
|
|
604
|
+
"modelItemCheck": "_3LBPpW_modelItemCheck",
|
|
605
|
+
"headText": "_3LBPpW_headText",
|
|
589
606
|
"modelItemCurrent": "_3LBPpW_modelItemCurrent",
|
|
607
|
+
"header": "_3LBPpW_header",
|
|
608
|
+
"chevronOpen": "_3LBPpW_chevronOpen",
|
|
609
|
+
"knob": "_3LBPpW_knob",
|
|
610
|
+
"modelMenuChevronOpen": "_3LBPpW_modelMenuChevronOpen",
|
|
611
|
+
"modelItemDefault": "_3LBPpW_modelItemDefault",
|
|
612
|
+
"modelItemLabel": "_3LBPpW_modelItemLabel",
|
|
590
613
|
"switch": "_3LBPpW_switch",
|
|
591
|
-
"
|
|
592
|
-
"
|
|
593
|
-
"
|
|
614
|
+
"blockTitle": "_3LBPpW_blockTitle",
|
|
615
|
+
"modelItem": "_3LBPpW_modelItem",
|
|
616
|
+
"modelInput": "_3LBPpW_modelInput",
|
|
617
|
+
"card": "_3LBPpW_card",
|
|
594
618
|
"cardOpen": "_3LBPpW_cardOpen",
|
|
595
|
-
"
|
|
619
|
+
"row": "_3LBPpW_row",
|
|
620
|
+
"modelSave": "_3LBPpW_modelSave",
|
|
621
|
+
"modelMenuUp": "_3LBPpW_modelMenuUp",
|
|
596
622
|
"saved": "_3LBPpW_saved",
|
|
597
|
-
"
|
|
598
|
-
"
|
|
599
|
-
"
|
|
600
|
-
"
|
|
601
|
-
"modelItemDefault": "_3LBPpW_modelItemDefault",
|
|
602
|
-
"body": "_3LBPpW_body",
|
|
603
|
-
"chevronOpen": "_3LBPpW_chevronOpen",
|
|
604
|
-
"blockTitle": "_3LBPpW_blockTitle",
|
|
605
|
-
"errorText": "_3LBPpW_errorText",
|
|
623
|
+
"hint": "_3LBPpW_hint",
|
|
624
|
+
"modelMenuChevron": "_3LBPpW_modelMenuChevron",
|
|
625
|
+
"switchOn": "_3LBPpW_switchOn",
|
|
626
|
+
"block": "_3LBPpW_block",
|
|
606
627
|
"nameRow": "_3LBPpW_nameRow",
|
|
607
|
-
"
|
|
608
|
-
"modelMenu": "_3LBPpW_modelMenu",
|
|
609
|
-
"modelItem": "_3LBPpW_modelItem",
|
|
610
|
-
"modelItemCheck": "_3LBPpW_modelItemCheck",
|
|
611
|
-
"header": "_3LBPpW_header",
|
|
628
|
+
"modelField": "_3LBPpW_modelField",
|
|
612
629
|
"info": "_3LBPpW_info",
|
|
613
|
-
"
|
|
614
|
-
"description": "_3LBPpW_description",
|
|
615
|
-
"switchOn": "_3LBPpW_switchOn",
|
|
616
|
-
"hint": "_3LBPpW_hint"
|
|
630
|
+
"name": "_3LBPpW_name"
|
|
617
631
|
};
|
|
618
632
|
//#endregion
|
|
619
633
|
//#region src/client/SettingsCard.tsx
|
|
@@ -959,8 +973,9 @@ window.__ModuleLoader__.load({
|
|
|
959
973
|
});
|
|
960
974
|
}
|
|
961
975
|
/**
|
|
962
|
-
* The dsh harness settings card
|
|
963
|
-
*
|
|
976
|
+
* The dsh harness settings card of the retired 0.1.5 plugin configuration
|
|
977
|
+
* tab (no longer registered; the live surface is the plugins.item entry
|
|
978
|
+
* below): the same collapsible chrome the official plugin cards use, re-implemented
|
|
964
979
|
* locally (the bundle-purity gate forbids value-importing the official chrome
|
|
965
980
|
* — the context-guard pattern), with the at-a-glance credential dot in the
|
|
966
981
|
* header. Explanation copy lives in hover/focus bubbles behind ⓘ anchors —
|
|
@@ -1012,10 +1027,12 @@ window.__ModuleLoader__.load({
|
|
|
1012
1027
|
});
|
|
1013
1028
|
}
|
|
1014
1029
|
/**
|
|
1015
|
-
* The
|
|
1016
|
-
*
|
|
1017
|
-
*
|
|
1018
|
-
*
|
|
1030
|
+
* The provider's card on the Plugins page (the `plugins.item` entry
|
|
1031
|
+
* client/index.ts registers while the Host serves the namespace AND the core
|
|
1032
|
+
* predates the family card): the page
|
|
1033
|
+
* draws the title, the icon, and the crumb itself, so the entry renders the
|
|
1034
|
+
* one-liner for the `summary` view and the bare form — with its own save
|
|
1035
|
+
* control — for `page`.
|
|
1019
1036
|
* @param props - the owner view, the injected scope/auth faces, and copy.
|
|
1020
1037
|
* @returns the entry.
|
|
1021
1038
|
*/
|
|
@@ -1034,21 +1051,59 @@ window.__ModuleLoader__.load({
|
|
|
1034
1051
|
t
|
|
1035
1052
|
});
|
|
1036
1053
|
}
|
|
1054
|
+
/**
|
|
1055
|
+
* The harness's section inside the core's「本机 Agent 委派」family card: the
|
|
1056
|
+
* same useCardState + CardBody the plugins.item page view renders — the
|
|
1057
|
+
* collapsible chrome belongs to the family card now. The delegation enable
|
|
1058
|
+
* switch keeps its semantics: it writes the scope, the host mounts/unmounts
|
|
1059
|
+
* the harness on the served value, and the section itself stays put.
|
|
1060
|
+
* @param props - the injected scope/auth faces, the forwarded session seat, and copy.
|
|
1061
|
+
* @returns the section body.
|
|
1062
|
+
*/
|
|
1063
|
+
function DshCardSection({ active, useSettings, scope, auth, authT, renderModelPicker, harnessModel, useSessions, t }) {
|
|
1064
|
+
const state = useCardState(active, useSettings, scope, harnessModel, t);
|
|
1065
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardBody, {
|
|
1066
|
+
state,
|
|
1067
|
+
auth,
|
|
1068
|
+
authT,
|
|
1069
|
+
renderModelPicker,
|
|
1070
|
+
useSessions,
|
|
1071
|
+
t
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1037
1074
|
//#endregion
|
|
1038
1075
|
//#region src/client/index.ts
|
|
1039
1076
|
/**
|
|
1040
|
-
*
|
|
1041
|
-
*
|
|
1042
|
-
*
|
|
1077
|
+
* Local-agent-dsh plugin, browser half: the dsh settings card — a section of
|
|
1078
|
+
* the family core's「本机 Agent 委派」family card on the Plugins page
|
|
1079
|
+
* (registered through the core's `localAgentUi.registerHarnessCard` while the
|
|
1080
|
+
* Host serves the `local-agent-dsh` namespace), or this package's own
|
|
1081
|
+
* `plugins.item` entry when the core predates the family card (the
|
|
1082
|
+
* declare-and-degrade fallback). Every
|
|
1083
|
+
* install shape serves the row's volatile Config under the row id —
|
|
1084
|
+
* standalone install, family bundle, or mode pack — so the card follows the
|
|
1085
|
+
* served namespace rather than any carrier bundle, and a composition without
|
|
1086
|
+
* the row shows no trace of it. The card carries the family core's shared
|
|
1087
|
+
* ProviderAuthBlock (dsh authenticates through the host credentials, so the
|
|
1088
|
+
* block renders status only), the DeepSeek delegation switch — migrated from
|
|
1089
|
+
* the core section's `local-agent.settings.row-action` seat, which this
|
|
1090
|
+
* plugin no longer contributes to — and the resident-mode block (live switch)
|
|
1091
|
+
* plus the default-model block (free-text input, the broker's effective-model
|
|
1092
|
+
* line and suggestion vocabulary), whose writes ride the bound settingsScope:
|
|
1093
|
+
* the host watcher hot-applies them, no reload. Every read degrades: an
|
|
1094
|
+
* absent gateway renders the auth block's 'unavailable' state and drops the
|
|
1095
|
+
* model surface to the bare input, an unregistered namespace disables the
|
|
1096
|
+
* controls.
|
|
1097
|
+
* @module @khorsheed/dsh-local-agent-dsh/client
|
|
1043
1098
|
*/
|
|
1044
|
-
const PACKAGE_NAME = "@khorsheed/dsh-local-agent-dsh";
|
|
1045
1099
|
/**
|
|
1046
1100
|
* Required services: slot registry, command Remote, and locale registry. The
|
|
1047
1101
|
* settings scope is NOT a static inject — its service name differs per host
|
|
1048
1102
|
* line (`settingsScope` on 0.1.5, `configForms` on rc.1), so a static entry
|
|
1049
1103
|
* would pend the whole client plugin on the other line; bindSettingsScope
|
|
1050
1104
|
* probes the serving one instead (the settings client package is a declared
|
|
1051
|
-
* client inject, so one of them is always already up).
|
|
1105
|
+
* client inject, so one of them is always already up). The card track probes
|
|
1106
|
+
* `configForms` through the same kind of deferred inject (see apply).
|
|
1052
1107
|
*/
|
|
1053
1108
|
const inject = [
|
|
1054
1109
|
"slots",
|
|
@@ -1057,7 +1112,7 @@ window.__ModuleLoader__.load({
|
|
|
1057
1112
|
"locale"
|
|
1058
1113
|
];
|
|
1059
1114
|
/**
|
|
1060
|
-
* Client plugin body: register the dictionaries and the dsh settings
|
|
1115
|
+
* Client plugin body: register the dictionaries and the dsh settings card
|
|
1061
1116
|
* bound to the local-agent-dsh namespace.
|
|
1062
1117
|
* @param ctx - client root context.
|
|
1063
1118
|
*/
|
|
@@ -1066,12 +1121,12 @@ window.__ModuleLoader__.load({
|
|
|
1066
1121
|
zh,
|
|
1067
1122
|
en
|
|
1068
1123
|
}), "local-agent-dsh: dictionaries");
|
|
1124
|
+
const t = ctx.locale.bind(NS);
|
|
1069
1125
|
const scope = bindSettingsScope(ctx, "local-agent-dsh");
|
|
1070
1126
|
const authT = ctx.locale.bind("local-agent");
|
|
1071
1127
|
const gateway = () => ctx.get("remote.localAgentGateway");
|
|
1072
|
-
const
|
|
1128
|
+
const cardFace = () => ({
|
|
1073
1129
|
scope,
|
|
1074
|
-
hooks: { settings: scope },
|
|
1075
1130
|
authT,
|
|
1076
1131
|
renderModelPicker: (props) => ctx.get("localAgentUi")?.renderHarnessModelPicker("dsh", props),
|
|
1077
1132
|
harnessModel: () => gateway()?.harnessModel("dsh").then((result) => result.ok ? result.value : void 0) ?? Promise.resolve(void 0),
|
|
@@ -1080,28 +1135,45 @@ window.__ModuleLoader__.load({
|
|
|
1080
1135
|
runCommand: (sessionId, line) => ctx.remote.commands.execute(sessionId, line, []).then((result) => result.ok ? result.value?.result.text : void 0)
|
|
1081
1136
|
}
|
|
1082
1137
|
});
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1138
|
+
const cardInject = () => ({
|
|
1139
|
+
...cardFace(),
|
|
1140
|
+
hooks: { settings: scope }
|
|
1141
|
+
});
|
|
1142
|
+
const subscribeScope = (listener) => scope.subscribe(listener);
|
|
1143
|
+
const readScope = () => scope.getSnapshot();
|
|
1144
|
+
const useSettings = (select) => select((0, react.useSyncExternalStore)(subscribeScope, readScope));
|
|
1145
|
+
ctx.inject(["configForms"], (formsCtx) => {
|
|
1146
|
+
const forms = formsCtx.configForms;
|
|
1147
|
+
if (typeof forms.whileServed !== "function") return;
|
|
1148
|
+
return forms.whileServed(["local-agent-dsh"], () => {
|
|
1149
|
+
const ui = ctx.get("localAgentUi");
|
|
1150
|
+
if (typeof ui?.registerHarnessCard === "function") return ui.registerHarnessCard({
|
|
1151
|
+
name: "dsh",
|
|
1152
|
+
order: 140,
|
|
1153
|
+
locale: NS,
|
|
1154
|
+
label: () => t("card.title"),
|
|
1155
|
+
render: ({ active, useSessions }) => (0, react.createElement)(DshCardSection, {
|
|
1156
|
+
active,
|
|
1157
|
+
useSessions,
|
|
1158
|
+
...cardFace(),
|
|
1159
|
+
useSettings,
|
|
1160
|
+
t
|
|
1161
|
+
})
|
|
1162
|
+
});
|
|
1163
|
+
return ctx.slots.inject("plugins.item", () => ctx.slots.register({
|
|
1164
|
+
name: "plugins.item",
|
|
1165
|
+
id: "local-agent-dsh",
|
|
1166
|
+
order: 140,
|
|
1167
|
+
label: () => t("card.title"),
|
|
1168
|
+
locale: NS,
|
|
1169
|
+
inject: cardInject
|
|
1170
|
+
}, DshBundleConfig));
|
|
1171
|
+
});
|
|
1172
|
+
});
|
|
1102
1173
|
}
|
|
1103
1174
|
//#endregion
|
|
1104
1175
|
exports.DshBundleConfig = DshBundleConfig;
|
|
1176
|
+
exports.DshCardSection = DshCardSection;
|
|
1105
1177
|
exports.DshSettingsCard = DshSettingsCard;
|
|
1106
1178
|
exports.NS = NS;
|
|
1107
1179
|
exports.apply = apply;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { provisionDshScope } from "./provision.js";
|
|
1
|
+
import { provisionDshScope, unitSubProfileName } from "./provision.js";
|
|
2
2
|
import { t as resolveDshSessionLog } from "./session-log-DOsSz56-.js";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
5
4
|
import { LiveFlush, LiveStreamPublisher, assertResumeCwdUnchanged, assertResumeScopeUnchanged, containerExecSpawn, containerScopedHome, delegationEnv, establishSubagentCatalogChild, persistChildSession, probeCliVersion, resolveChildCwd, resolveRoundModel, subagentDelegationLabel } from "@khorsheed/dsh-local-agent";
|
|
6
|
-
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
6
|
+
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
7
7
|
import { SessionId } from "@deepseek-ai/dsh-session";
|
|
8
|
-
import { NO_START_CAPABILITIES, settleRunResult, subprocessRunHandle } from "@deepseek-ai/dsh-subagent";
|
|
8
|
+
import { NO_START_CAPABILITIES, SUBAGENT_DESCRIPTOR_VERSION, settleRunResult, subprocessRunHandle } from "@deepseek-ai/dsh-subagent";
|
|
9
9
|
import { MEMBER_BRIDGE_SOCKET_ENV, MEMBER_BRIDGE_TOKEN_ENV } from "@khorsheed/dsh-local-agent/types";
|
|
10
10
|
import { StringDecoder } from "node:string_decoder";
|
|
11
11
|
import { BlockAssembler, createAssistantMessage } from "@deepseek-ai/dsh-llm";
|
|
@@ -13,6 +13,67 @@ import { LIVE_SERVER_NAME, LIVE_WIRE_PROTOCOL_VERSION } from "@khorsheed/dsh-loc
|
|
|
13
13
|
import { readFile, readdir } from "node:fs/promises";
|
|
14
14
|
import { promisify } from "node:util";
|
|
15
15
|
import { zstdDecompress } from "node:zlib";
|
|
16
|
+
//#region lib/types/model-launch.js
|
|
17
|
+
/** Private launch environment: never persisted in a profile, transcript or argv. */
|
|
18
|
+
const MODEL_CONFIG_ENV = "DSH_LOCAL_AGENT_MODEL_CONFIG";
|
|
19
|
+
const MODEL_KEY_ENV = "DSH_LOCAL_AGENT_MODEL_API_KEY";
|
|
20
|
+
function object(value) {
|
|
21
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
22
|
+
}
|
|
23
|
+
function settings(ctx) {
|
|
24
|
+
return new Map((ctx.get("settings")?.describe?.() ?? []).flatMap((row) => {
|
|
25
|
+
const value = object(row.value);
|
|
26
|
+
return value === void 0 ? [] : [[String(row.ns), value]];
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
function piProfile(ctx, provider, values) {
|
|
30
|
+
const directory = ctx.get("llm")?.listConfigurableProviders?.().find((row) => row.provider === provider);
|
|
31
|
+
const ns = directory?.settingsNs ?? "llm-pi-ai";
|
|
32
|
+
if (ns !== "llm-pi-ai") return void 0;
|
|
33
|
+
if (directory && (directory.settingsPath.length !== 2 || directory.settingsPath[0] !== "providers" || directory.settingsPath[1] !== provider)) return void 0;
|
|
34
|
+
return object(object(values.get(ns)?.providers)?.[provider]);
|
|
35
|
+
}
|
|
36
|
+
/** Whether this provider has a supported local configuration transfer contract. */
|
|
37
|
+
function canInheritModelProvider(ctx, provider) {
|
|
38
|
+
if (provider === "deepseek-official") return true;
|
|
39
|
+
const profile = piProfile(ctx, provider, settings(ctx));
|
|
40
|
+
return typeof profile?.apiKeyEnv === "string" && profile.apiKeyEnv.trim() !== "";
|
|
41
|
+
}
|
|
42
|
+
/** Snapshot only the selected local route and resolve only its explicitly named credential. */
|
|
43
|
+
async function localModelLaunch(ctx, config, model) {
|
|
44
|
+
const provider = model?.includes("/") ? model.slice(0, model.indexOf("/")) : "deepseek-official";
|
|
45
|
+
const values = settings(ctx);
|
|
46
|
+
let inherited, ref, keyName;
|
|
47
|
+
if (provider === "deepseek-official") {
|
|
48
|
+
const profile = structuredClone(values.get("llm-deepseek") ?? {});
|
|
49
|
+
ref = config.apiKeyRef ?? (typeof profile.apiKeyEnv === "string" ? profile.apiKeyEnv : "DEEPSEEK_API_KEY");
|
|
50
|
+
inherited = { deepseek: {
|
|
51
|
+
...profile,
|
|
52
|
+
apiKeyEnv: "DEEPSEEK_API_KEY"
|
|
53
|
+
} };
|
|
54
|
+
keyName = "DEEPSEEK_API_KEY";
|
|
55
|
+
} else {
|
|
56
|
+
const profile = piProfile(ctx, provider, values);
|
|
57
|
+
if (typeof profile?.apiKeyEnv !== "string" || profile.apiKeyEnv.trim() === "") throw new Error(`subagent-dsh: model provider "${provider}" has no inheritable API-key route; configure its credential reference in the host Models settings`);
|
|
58
|
+
ref = profile.apiKeyEnv;
|
|
59
|
+
inherited = { piAi: { providers: { [provider]: {
|
|
60
|
+
...structuredClone(profile),
|
|
61
|
+
apiKeyEnv: MODEL_KEY_ENV
|
|
62
|
+
} } } };
|
|
63
|
+
keyName = MODEL_KEY_ENV;
|
|
64
|
+
}
|
|
65
|
+
const credential = await ctx.get("credentials")?.resolve(credentialRef(ref));
|
|
66
|
+
if (!credential?.value) throw new Error(`subagent-dsh: credential ${ref} is unavailable for model provider "${provider}"`);
|
|
67
|
+
const env = {
|
|
68
|
+
[MODEL_CONFIG_ENV]: JSON.stringify(inherited),
|
|
69
|
+
[keyName]: credential.value
|
|
70
|
+
};
|
|
71
|
+
return {
|
|
72
|
+
env,
|
|
73
|
+
fingerprint: createHash("sha256").update(JSON.stringify(env)).digest("hex")
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
16
77
|
//#region lib/types/session-mirror.js
|
|
17
78
|
/**
|
|
18
79
|
* Mirror a sub-dsh session's events into the parent-side dsh subagent session,
|
|
@@ -428,6 +489,7 @@ var LiveRuntime = class {
|
|
|
428
489
|
* spawned with no model flag at all. A round whose start model differs
|
|
429
490
|
* retires the runtime instead of silently running the old one.
|
|
430
491
|
*/
|
|
492
|
+
modelLaunchFingerprint;
|
|
431
493
|
configurationKey;
|
|
432
494
|
boundModel;
|
|
433
495
|
reclaimed = false;
|
|
@@ -695,12 +757,12 @@ var DshLiveDriver = class {
|
|
|
695
757
|
* The member's live runtime, spawning it (once per member at a time) when
|
|
696
758
|
* absent or dead. `signal` abandons the spawn on cancellation.
|
|
697
759
|
*/
|
|
698
|
-
async ensureRuntime(spec,
|
|
760
|
+
async ensureRuntime(spec, launch, signal) {
|
|
699
761
|
const key = spec.sessionId;
|
|
700
762
|
const startModel = spec.startModel?.trim();
|
|
701
763
|
const existing = this.runtimes.get(key);
|
|
702
764
|
if (existing !== void 0 && !existing.dead) {
|
|
703
|
-
if (spec.configuration !== void 0 && existing.configurationKey !== JSON.stringify(spec.configuration) || spec.configuration === void 0 && existing.configurationKey !== void 0 || startModel !== void 0 && startModel !== "" && existing.boundModel !== startModel) await this.reclaim(key);
|
|
765
|
+
if (existing.modelLaunchFingerprint !== launch.fingerprint || spec.configuration !== void 0 && existing.configurationKey !== JSON.stringify(spec.configuration) || spec.configuration === void 0 && existing.configurationKey !== void 0 || startModel !== void 0 && startModel !== "" && existing.boundModel !== startModel) await this.reclaim(key);
|
|
704
766
|
else {
|
|
705
767
|
this.clearIdleTimer(key);
|
|
706
768
|
return existing;
|
|
@@ -708,7 +770,7 @@ var DshLiveDriver = class {
|
|
|
708
770
|
} else if (existing !== void 0) this.runtimes.delete(key);
|
|
709
771
|
const pending = this.ensuring.get(key);
|
|
710
772
|
if (pending !== void 0) return pending;
|
|
711
|
-
const spawn = this.spawnRuntime(spec,
|
|
773
|
+
const spawn = this.spawnRuntime(spec, launch, signal).finally(() => {
|
|
712
774
|
this.ensuring.delete(key);
|
|
713
775
|
});
|
|
714
776
|
this.ensuring.set(key, spawn);
|
|
@@ -722,7 +784,7 @@ var DshLiveDriver = class {
|
|
|
722
784
|
* cancellation mid-spawn reclaims the half-started runtime and throws the
|
|
723
785
|
* abort instead, without touching the breaker.
|
|
724
786
|
*/
|
|
725
|
-
async spawnRuntime(spec,
|
|
787
|
+
async spawnRuntime(spec, launch, signal) {
|
|
726
788
|
const key = spec.sessionId;
|
|
727
789
|
if (this.disposed) throw new LiveChannelUnavailableError("the live driver is disposed");
|
|
728
790
|
const profileName = this.config.profileName ?? "headless-local-agent-dsh";
|
|
@@ -747,7 +809,7 @@ var DshLiveDriver = class {
|
|
|
747
809
|
graceMs: DEFAULT_DISPOSE_GRACE_MS,
|
|
748
810
|
env: delegationEnv({
|
|
749
811
|
DSH_HOME: spec.homeDir,
|
|
750
|
-
|
|
812
|
+
...launch.env,
|
|
751
813
|
...process.env.TSX_TSCONFIG_PATH === void 0 ? {} : { TSX_TSCONFIG_PATH: process.env.TSX_TSCONFIG_PATH },
|
|
752
814
|
...member === void 0 ? {} : member.env
|
|
753
815
|
})
|
|
@@ -764,6 +826,7 @@ var DshLiveDriver = class {
|
|
|
764
826
|
this.ctx.logger.warn(message);
|
|
765
827
|
});
|
|
766
828
|
runtime.boundModel = model === void 0 || model === "" ? void 0 : model;
|
|
829
|
+
runtime.modelLaunchFingerprint = launch.fingerprint;
|
|
767
830
|
runtime.configurationKey = spec.configuration === void 0 ? void 0 : JSON.stringify(spec.configuration);
|
|
768
831
|
runtime.onDead = () => {
|
|
769
832
|
if (this.runtimes.get(key) === runtime) this.runtimes.delete(key);
|
|
@@ -821,9 +884,12 @@ var DshLiveDriver = class {
|
|
|
821
884
|
signal.throwIfAborted();
|
|
822
885
|
if (this.draining || this.disabled) throw new LiveChannelUnavailableError("the live driver is unavailable for preparation");
|
|
823
886
|
try {
|
|
824
|
-
provisionDshScope(spec.homeDir,
|
|
825
|
-
|
|
826
|
-
|
|
887
|
+
provisionDshScope(spec.homeDir, {
|
|
888
|
+
...this.config,
|
|
889
|
+
inheritModelConfiguration: true
|
|
890
|
+
});
|
|
891
|
+
const launch = await localModelLaunch(this.ctx, this.config, spec.configuration?.model ?? spec.startModel ?? this.config.modelFor?.(spec.sessionId));
|
|
892
|
+
await (await this.ensureRuntime(spec, launch, signal)).request("session/prepare", {
|
|
827
893
|
sessionId: spec.sessionId,
|
|
828
894
|
resume: spec.resume !== void 0
|
|
829
895
|
});
|
|
@@ -942,6 +1008,7 @@ var DshLiveDriver = class {
|
|
|
942
1008
|
for (const entry of bufferedEvents.splice(0)) if (entry.kind === "event") mirrorOne(entry.event);
|
|
943
1009
|
else acceptStream(entry.frame);
|
|
944
1010
|
};
|
|
1011
|
+
let nativeFailure;
|
|
945
1012
|
let resolveIdle;
|
|
946
1013
|
const idle = new Promise((resolve) => {
|
|
947
1014
|
resolveIdle = resolve;
|
|
@@ -971,10 +1038,13 @@ var DshLiveDriver = class {
|
|
|
971
1038
|
resolveIdle({ reason });
|
|
972
1039
|
};
|
|
973
1040
|
};
|
|
974
|
-
provisionDshScope(spec.homeDir,
|
|
975
|
-
|
|
1041
|
+
provisionDshScope(spec.homeDir, {
|
|
1042
|
+
...this.config,
|
|
1043
|
+
inheritModelConfiguration: true
|
|
1044
|
+
});
|
|
1045
|
+
const launch = await localModelLaunch(this.ctx, this.config, spec.configuration?.model ?? spec.startModel ?? this.config.modelFor?.(spec.sessionId));
|
|
976
1046
|
const accepted = (async () => {
|
|
977
|
-
const rt = await this.ensureRuntime(spec,
|
|
1047
|
+
const rt = await this.ensureRuntime(spec, launch, request.signal);
|
|
978
1048
|
runtime = rt;
|
|
979
1049
|
if (runAbort.signal.aborted) {
|
|
980
1050
|
await this.reclaim(spec.sessionId);
|
|
@@ -1004,7 +1074,13 @@ var DshLiveDriver = class {
|
|
|
1004
1074
|
output: collectOutput(),
|
|
1005
1075
|
stopReason: "aborted"
|
|
1006
1076
|
};
|
|
1007
|
-
if (reason?.kind === "error")
|
|
1077
|
+
if (reason?.kind === "error") {
|
|
1078
|
+
nativeFailure = {
|
|
1079
|
+
message: reason.error.message,
|
|
1080
|
+
code: reason.error.code
|
|
1081
|
+
};
|
|
1082
|
+
throw new Error(`subagent-dsh live: the turn failed: ${reason.error.message}`);
|
|
1083
|
+
}
|
|
1008
1084
|
if (reason?.kind !== "completed") throw new Error("subagent-dsh live: the turn produced no outcome");
|
|
1009
1085
|
const output = collectOutput();
|
|
1010
1086
|
if (output.length === 0) throw new Error("subagent-dsh live: the turn completed but produced no answer");
|
|
@@ -1068,7 +1144,7 @@ var DshLiveDriver = class {
|
|
|
1068
1144
|
turn,
|
|
1069
1145
|
reason: {
|
|
1070
1146
|
kind: "error",
|
|
1071
|
-
error: {
|
|
1147
|
+
error: nativeFailure ?? {
|
|
1072
1148
|
message: "the live round did not complete",
|
|
1073
1149
|
code: "UNKNOWN"
|
|
1074
1150
|
}
|
|
@@ -1118,7 +1194,7 @@ var DshLiveDriver = class {
|
|
|
1118
1194
|
});
|
|
1119
1195
|
await Promise.race([accepted, abortBranch.catch(() => void 0)]);
|
|
1120
1196
|
return subprocessRunHandle({
|
|
1121
|
-
id:
|
|
1197
|
+
id: SessionId(randomUUID()),
|
|
1122
1198
|
result,
|
|
1123
1199
|
signal: request.signal,
|
|
1124
1200
|
onAbort,
|
|
@@ -1272,7 +1348,7 @@ var DshCliProvider = class {
|
|
|
1272
1348
|
...record?.configurationLock === void 0 ? {} : { configurationLock: record.configurationLock }
|
|
1273
1349
|
}, (configuration) => this.startDshResume(request, intent, cwd, homeDir, exec, scope, record?.model, configuration), request.signal, intent?.onAdmitted);
|
|
1274
1350
|
}
|
|
1275
|
-
const childSessionId = SessionId(intent?.preparedMemberId ?? randomUUID());
|
|
1351
|
+
const childSessionId = SessionId(intent?.preparedMemberId ?? intent?.childSessionId ?? randomUUID());
|
|
1276
1352
|
if (typeof this.ctx.localAgent.withMemberConfigurationRound !== "function") {
|
|
1277
1353
|
if (intent?.effort !== void 0) throw new Error("DSH effort requires the configuration admission core");
|
|
1278
1354
|
return this.startDshFresh(request, cwd, homeDir, exec, scope, intent?.model, childSessionId);
|
|
@@ -1301,9 +1377,11 @@ var DshCliProvider = class {
|
|
|
1301
1377
|
delegationDepth: (request.parent.session.header.delegationDepth ?? 0) + 1
|
|
1302
1378
|
} });
|
|
1303
1379
|
const harness = this.ctx.localAgent.get("dsh");
|
|
1304
|
-
const label = subagentDelegationLabel(harness?.displayName ?? "dsh", request.
|
|
1380
|
+
const label = subagentDelegationLabel(harness?.displayName ?? "dsh", request.label || void 0);
|
|
1305
1381
|
childSession.append("subagent/descriptor", {
|
|
1306
|
-
|
|
1382
|
+
version: SUBAGENT_DESCRIPTOR_VERSION,
|
|
1383
|
+
mode: "one-shot",
|
|
1384
|
+
provider: this.name,
|
|
1307
1385
|
label
|
|
1308
1386
|
});
|
|
1309
1387
|
establishSubagentCatalogChild(request.parent.session, childSession.header, label);
|
|
@@ -1505,9 +1583,12 @@ async function startDshCliRun(request, spec) {
|
|
|
1505
1583
|
const { config, ctx } = spec;
|
|
1506
1584
|
const task = dshTextTask(request.prompt);
|
|
1507
1585
|
if (request.signal.aborted) throw new Error("subagent-dsh: request was aborted before the CLI started");
|
|
1508
|
-
const profileName = config.profileName ?? "headless-local-agent-dsh";
|
|
1509
|
-
if (spec.exec === void 0) provisionDshScope(spec.homeDir,
|
|
1510
|
-
|
|
1586
|
+
const profileName = spec.exec === void 0 ? config.profileName ?? "headless-local-agent-dsh" : unitSubProfileName(config);
|
|
1587
|
+
if (spec.exec === void 0) provisionDshScope(spec.homeDir, {
|
|
1588
|
+
...config,
|
|
1589
|
+
inheritModelConfiguration: true
|
|
1590
|
+
});
|
|
1591
|
+
const modelEnv = spec.exec === void 0 ? (await localModelLaunch(ctx, config, spec.model)).env : { DEEPSEEK_API_KEY: await resolveApiKey(ctx, config) };
|
|
1511
1592
|
const turn = spec.resume?.turn ?? 1;
|
|
1512
1593
|
spec.childSession?.append("turn/start", { turn });
|
|
1513
1594
|
const launch = dshLaunchArgv(config);
|
|
@@ -1531,7 +1612,7 @@ async function startDshCliRun(request, spec) {
|
|
|
1531
1612
|
];
|
|
1532
1613
|
const env = delegationEnv({
|
|
1533
1614
|
DSH_HOME: spec.homeDir,
|
|
1534
|
-
|
|
1615
|
+
...modelEnv,
|
|
1535
1616
|
...spec.exec !== void 0 || process.env.TSX_TSCONFIG_PATH === void 0 ? {} : { TSX_TSCONFIG_PATH: process.env.TSX_TSCONFIG_PATH },
|
|
1536
1617
|
...spec.exec === void 0 || spec.exec.env?.["NODE_OPTIONS"] !== void 0 ? {} : { NODE_OPTIONS: CONTAINER_NODE_OPTIONS },
|
|
1537
1618
|
...spec.memberEnv
|
|
@@ -1682,7 +1763,7 @@ async function startDshCliRun(request, spec) {
|
|
|
1682
1763
|
}
|
|
1683
1764
|
}, () => {});
|
|
1684
1765
|
return Promise.resolve(subprocessRunHandle({
|
|
1685
|
-
id:
|
|
1766
|
+
id: SessionId(randomUUID()),
|
|
1686
1767
|
result,
|
|
1687
1768
|
signal: request.signal,
|
|
1688
1769
|
onAbort,
|
|
@@ -1698,4 +1779,4 @@ async function resolveApiKey(ctx, config) {
|
|
|
1698
1779
|
return resolved.value;
|
|
1699
1780
|
}
|
|
1700
1781
|
//#endregion
|
|
1701
|
-
export { MEMBER_BRIDGE_ENTRY_ENV as a, dshTextTask as c, startDshCliRun as d, DEFAULT_LIVE_IDLE_MS as f, DshCliProvider as i, registerMemberRun as l, DEFAULT_DISPOSE_GRACE_MS as n, dshCliVersion as o, DshLiveDriver as p, DEFAULT_LIVE_MIRROR_INTERVAL_MS as r, dshLaunchArgv as s, CONTAINER_NODE_OPTIONS as t, resolveApiKey as u };
|
|
1782
|
+
export { MEMBER_BRIDGE_ENTRY_ENV as a, dshTextTask as c, startDshCliRun as d, DEFAULT_LIVE_IDLE_MS as f, localModelLaunch as h, DshCliProvider as i, registerMemberRun as l, canInheritModelProvider as m, DEFAULT_DISPOSE_GRACE_MS as n, dshCliVersion as o, DshLiveDriver as p, DEFAULT_LIVE_MIRROR_INTERVAL_MS as r, dshLaunchArgv as s, CONTAINER_NODE_OPTIONS as t, resolveApiKey as u };
|