@klarkxy/dsh-current-title 0.2.5 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/docs/README.zh-CN.md +3 -2
- package/lib/client.inner.cjs +123 -56
- package/lib/client.inner.cjs.map +1 -1
- package/lib/client.js +123 -56
- package/lib/contracts.d.ts +1 -1
- package/lib/index.d.ts +2 -0
- package/lib/index.js +22 -12
- package/lib/index.js.map +1 -1
- package/package.json +17 -12
package/lib/client.js
CHANGED
|
@@ -298,6 +298,17 @@ ${scope} {
|
|
|
298
298
|
/* --- Layout scaffolding. --- */
|
|
299
299
|
& .dsh-ui-stack { display: flex; flex-direction: column; gap: 12px; }
|
|
300
300
|
& .dsh-ui-stack-lg { display: flex; flex-direction: column; gap: 20px; }
|
|
301
|
+
/* Plugin detail pages share one reading column. These are layout recipes;
|
|
302
|
+
* controls keep their own primitive styles. Form spacing follows the host's
|
|
303
|
+
* settings-form/fields.module.css and SettingsForm.module.css. */
|
|
304
|
+
& .dsh-ui-page { display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 760px; min-width: 0; }
|
|
305
|
+
& .dsh-ui-form { display: flex; flex-direction: column; gap: 0; min-width: 0; }
|
|
306
|
+
&.dsh-ui-form > .dsh-ui-field, & .dsh-ui-form > .dsh-ui-field { padding: 12px 0; }
|
|
307
|
+
& .dsh-ui-form-footer { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding-top: 16px; min-width: 0; }
|
|
308
|
+
& .dsh-ui-fields-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 20px; min-width: 0; margin: 0; padding: 0; border: 0; }
|
|
309
|
+
@media (max-width: 560px) {
|
|
310
|
+
& .dsh-ui-fields-grid { grid-template-columns: minmax(0, 1fr); }
|
|
311
|
+
}
|
|
301
312
|
& .dsh-ui-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
|
302
313
|
& .dsh-ui-row-wrap { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
|
|
303
314
|
& .dsh-ui-spacer { flex: 1; min-width: 0; }
|
|
@@ -325,6 +336,9 @@ ${scope} {
|
|
|
325
336
|
* official primitive, so this only owns the vertical rhythm. --- */
|
|
326
337
|
& .dsh-ui-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
|
|
327
338
|
& .dsh-ui-field + .dsh-ui-field { border-top: 0.5px solid var(--dsw-alias-border-l2); padding-top: 12px; }
|
|
339
|
+
/* Grid fields share a row, so the stacked-field divider must not win later in
|
|
340
|
+
* the cascade. Reset it here, after the field recipe. */
|
|
341
|
+
& .dsh-ui-fields-grid > .dsh-ui-field { padding: 0; border: 0; }
|
|
328
342
|
& .dsh-ui-label { display: flex; align-items: center; gap: 6px; font-size: 13px; line-height: 20px; font-weight: 500; color: var(--dsw-alias-label-primary); }
|
|
329
343
|
& .dsh-ui-label-row { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
|
|
330
344
|
& .dsh-ui-help { font-size: 12px; line-height: 1.6; color: var(--dsw-alias-label-secondary); }
|
|
@@ -515,29 +529,22 @@ ${scope} {
|
|
|
515
529
|
& *, & *::before, & *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
|
|
516
530
|
}
|
|
517
531
|
}
|
|
518
|
-
`.replace(/^& (\.dsh-ui-[a-z0-9-]+)(
|
|
532
|
+
`.replace(/^& (\.dsh-ui-[a-z0-9-]+)(?=\s*\{)/gm, (_match, className) => `&${className}, & ${className}`);
|
|
519
533
|
}
|
|
520
534
|
//#endregion
|
|
521
535
|
//#region ../dsh-plugin-kit/src/contracts.ts
|
|
522
|
-
/**
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
method: "watch",
|
|
535
|
-
mode: "stream",
|
|
536
|
-
invocation: { kind: "direct" },
|
|
537
|
-
parameters: [],
|
|
538
|
-
cancellation: { parameter: "signal" }
|
|
539
|
-
}]
|
|
540
|
-
};
|
|
536
|
+
/** `@klarkxy/dsh-git-commit` becomes `KlarkxyDshGitCommitModelSettingsChanges`. */
|
|
537
|
+
function modelSettingsChangesIdentity(packageName) {
|
|
538
|
+
const scoped = /^@([^/]+)\/(.+)$/.exec(packageName);
|
|
539
|
+
const parts = (scoped ? `${scoped[1]}/${scoped[2]}` : packageName).split(/[^A-Za-z0-9]+/).filter(Boolean);
|
|
540
|
+
if (!parts.length) throw new Error("Model settings package name has no RPC segment");
|
|
541
|
+
const segment = `${parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("")}ModelSettingsChanges`;
|
|
542
|
+
if (!/^[A-Z][A-Za-z0-9]*$/.test(segment)) throw new Error(`Model settings sync name is not an RPC segment: ${segment}`);
|
|
543
|
+
return {
|
|
544
|
+
service: segment,
|
|
545
|
+
namespace: segment
|
|
546
|
+
};
|
|
547
|
+
}
|
|
541
548
|
//#endregion
|
|
542
549
|
//#region ../dsh-plugin-kit/src/model-settings-client.ts
|
|
543
550
|
/** This stream carries invalidations only; ready is never a settings snapshot. */
|
|
@@ -552,17 +559,44 @@ function parseModelSettingsChangeFrame(value) {
|
|
|
552
559
|
};
|
|
553
560
|
throw new Error("Invalid model settings change frame");
|
|
554
561
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
562
|
+
/** One owner's contribution. Each package mounts only its own endpoint. */
|
|
563
|
+
function modelSettingsChangesRemote(packageName, identity = modelSettingsChangesIdentity(packageName)) {
|
|
564
|
+
return {
|
|
565
|
+
package: packageName,
|
|
566
|
+
descriptors: [{
|
|
567
|
+
id: `${identity.namespace}/watch`,
|
|
568
|
+
service: identity.service,
|
|
569
|
+
namespace: identity.namespace,
|
|
570
|
+
method: "watch",
|
|
571
|
+
mode: "stream",
|
|
572
|
+
invocation: { kind: "direct" },
|
|
573
|
+
parameters: [],
|
|
574
|
+
cancellation: { parameter: "signal" },
|
|
575
|
+
result: {
|
|
576
|
+
mode: "strict",
|
|
577
|
+
typeSymbol: "ModelSettings.ChangeFrame",
|
|
578
|
+
create: () => ({ parse: parseModelSettingsChangeFrame })
|
|
579
|
+
}
|
|
580
|
+
}]
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
/** Mount from a synchronous apply. Disposal waits for an in-flight mount and
|
|
584
|
+
* then withdraws it, so the contribution cannot outlive the plugin. */
|
|
585
|
+
function mountModelSettingsChanges(ctx, packageName) {
|
|
586
|
+
const contribution = modelSettingsChangesRemote(packageName);
|
|
587
|
+
ctx.inject(["remote"], (injected) => {
|
|
588
|
+
const scope = injected;
|
|
589
|
+
const mounting = scope.remote.$mount(contribution);
|
|
590
|
+
mounting.catch(() => {});
|
|
591
|
+
scope.effect(() => async () => {
|
|
592
|
+
await (await mounting.catch(() => void 0))?.();
|
|
593
|
+
});
|
|
594
|
+
});
|
|
595
|
+
}
|
|
563
596
|
/** Native RemoteStream owns carrier reconnects; no timers or browser transport.
|
|
564
597
|
* A factory shares one logical stream while at least one subscriber remains. */
|
|
565
|
-
function createModelSettingsSubscriber(ctx,
|
|
598
|
+
function createModelSettingsSubscriber(ctx, packageName, identity = modelSettingsChangesIdentity(packageName), accept = (ns) => ns === packageName) {
|
|
599
|
+
const endpoint = identity;
|
|
566
600
|
const listeners = /* @__PURE__ */ new Set();
|
|
567
601
|
let injection;
|
|
568
602
|
let current;
|
|
@@ -588,12 +622,18 @@ function createModelSettingsSubscriber(ctx, namespace) {
|
|
|
588
622
|
};
|
|
589
623
|
const start = () => {
|
|
590
624
|
const life = ++lifecycle;
|
|
591
|
-
injection = ctx.inject(["remote",
|
|
625
|
+
injection = ctx.inject(["remote", `remote.${endpoint.namespace}`], (injected) => {
|
|
592
626
|
if (life !== lifecycle || !listeners.size) return;
|
|
593
627
|
const scope = injected;
|
|
628
|
+
const service = scope.remote[endpoint.namespace];
|
|
629
|
+
if (!service || !("watch" in service) || !service.watch) {
|
|
630
|
+
report("Model settings sync service unavailable");
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
const watch = service.watch;
|
|
594
634
|
const stream = scope.remote.$stream({
|
|
595
635
|
name: "Model settings sync",
|
|
596
|
-
open: (signal) =>
|
|
636
|
+
open: (signal) => watch(signal),
|
|
597
637
|
ended: (accepted) => /* @__PURE__ */ new Error(`Model settings sync stream ended ${accepted ? "after" : "before"} ready; refresh or reopen the page`)
|
|
598
638
|
});
|
|
599
639
|
const session = {
|
|
@@ -628,7 +668,7 @@ function createModelSettingsSubscriber(ctx, namespace) {
|
|
|
628
668
|
invalidate();
|
|
629
669
|
} else {
|
|
630
670
|
if (!ready) throw new Error("Model settings sync change arrived before ready");
|
|
631
|
-
if (
|
|
671
|
+
if (accept(frame.ns)) invalidate();
|
|
632
672
|
}
|
|
633
673
|
}
|
|
634
674
|
} catch (error) {
|
|
@@ -933,7 +973,14 @@ function useModelCatalog(load, options = {}) {
|
|
|
933
973
|
refresh
|
|
934
974
|
};
|
|
935
975
|
}
|
|
936
|
-
/**
|
|
976
|
+
/**
|
|
977
|
+
* Also accepts a mapped state from a caller that owns broader async orchestration.
|
|
978
|
+
*
|
|
979
|
+
* The dsh-ui-* classes carry the styling and resolve through the consuming
|
|
980
|
+
* plugin's own officialUiCss scope (every consumer mounts its root class, so
|
|
981
|
+
* e.g. `.dsh-model-hub .dsh-ui-loading` paints this); dsh-model-catalog-status
|
|
982
|
+
* stays as a structural hook for tests and layout, never a colour source.
|
|
983
|
+
*/
|
|
937
984
|
function ModelCatalogStatus({ state, locale }) {
|
|
938
985
|
const zh = locale?.toLowerCase().startsWith("zh") === true;
|
|
939
986
|
const copy = zh ? {
|
|
@@ -949,15 +996,17 @@ function ModelCatalogStatus({ state, locale }) {
|
|
|
949
996
|
};
|
|
950
997
|
if (!state.loading && !state.error && state.failures.length === 0) return null;
|
|
951
998
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
952
|
-
className: "dsh-model-catalog-status",
|
|
999
|
+
className: "dsh-model-catalog-status dsh-ui-stack",
|
|
953
1000
|
lang: zh ? "zh" : "en",
|
|
954
1001
|
children: [
|
|
955
1002
|
state.loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
956
1003
|
role: "status",
|
|
1004
|
+
className: "dsh-ui-loading",
|
|
957
1005
|
children: copy.loading
|
|
958
1006
|
}) : null,
|
|
959
1007
|
state.error ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
960
1008
|
role: "alert",
|
|
1009
|
+
className: "dsh-ui-error dsh-ui-wrap",
|
|
961
1010
|
children: [
|
|
962
1011
|
copy.error,
|
|
963
1012
|
": ",
|
|
@@ -966,20 +1015,27 @@ function ModelCatalogStatus({ state, locale }) {
|
|
|
966
1015
|
}) : null,
|
|
967
1016
|
state.failures.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
968
1017
|
role: "status",
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
failure.
|
|
973
|
-
|
|
1018
|
+
className: "dsh-ui-meta",
|
|
1019
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: copy.partial }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
1020
|
+
className: "dsh-ui-list",
|
|
1021
|
+
children: state.failures.map((failure, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
|
|
1022
|
+
failure.name || failure.id,
|
|
1023
|
+
": ",
|
|
1024
|
+
failure.message
|
|
1025
|
+
] }, `${failure.id}:${index}`))
|
|
1026
|
+
})]
|
|
974
1027
|
}) : null,
|
|
975
|
-
state.error || state.failures.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
state.
|
|
981
|
-
|
|
982
|
-
|
|
1028
|
+
state.error || state.failures.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1029
|
+
className: "dsh-ui-actions",
|
|
1030
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1031
|
+
type: "button",
|
|
1032
|
+
size: "sm",
|
|
1033
|
+
disabled: state.loading,
|
|
1034
|
+
onClick: () => {
|
|
1035
|
+
state.refresh();
|
|
1036
|
+
},
|
|
1037
|
+
children: copy.retry
|
|
1038
|
+
})
|
|
983
1039
|
}) : null
|
|
984
1040
|
]
|
|
985
1041
|
});
|
|
@@ -999,7 +1055,8 @@ function ModelCatalogStatus({ state, locale }) {
|
|
|
999
1055
|
* Sticky group headings, subsequence ranking, highlight walking and focus
|
|
1000
1056
|
* hand-back all match the host. SYNC POINT: when the host's ModelSelect or
|
|
1001
1057
|
* ModelSelect.module.css changes, this copy must be re-transcribed; styles
|
|
1002
|
-
* below may only reference
|
|
1058
|
+
* below may only reference allowlisted theme tokens (the --dsw-* set plus the
|
|
1059
|
+
* host's --ds-* motion tokens), never literal colors.
|
|
1003
1060
|
*
|
|
1004
1061
|
* It statically imports the primitives, so only browser clients should load
|
|
1005
1062
|
* this entry; host code and node tests use the core entry (`./index.ts`).
|
|
@@ -1507,7 +1564,8 @@ function ModelMenu(props) {
|
|
|
1507
1564
|
* sticky group headings (MenuGroup's own CSS), and the l1-stroke prominent
|
|
1508
1565
|
* elevation at portal z-index 1100. The trigger mirrors the composer's 28px
|
|
1509
1566
|
* model pill, chevron rotating open. MenuSurface paints fill and backdrop.
|
|
1510
|
-
* Only --dsw
|
|
1567
|
+
* Only allowlisted --dsw- and --ds- theme tokens may appear here — a literal
|
|
1568
|
+
* color is a bug.
|
|
1511
1569
|
*/
|
|
1512
1570
|
const modelMenuCss = `
|
|
1513
1571
|
.dsh-model-menu-root { min-width: 0; position: relative; display: inline-flex; }
|
|
@@ -1530,12 +1588,14 @@ const modelMenuCss = `
|
|
|
1530
1588
|
display: flex;
|
|
1531
1589
|
}
|
|
1532
1590
|
.dsh-model-menu-trigger:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }
|
|
1533
|
-
.dsh-model-menu-trigger:focus-visible:not([data-selection-focus]) { box-shadow: 0 0 0
|
|
1591
|
+
.dsh-model-menu-trigger:focus-visible:not([data-selection-focus]) { box-shadow: 0 0 0 var(--dsw-focus-ring-width) var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary)); }
|
|
1534
1592
|
.dsh-model-menu-trigger:disabled { color: var(--dsw-alias-label-dimmed); cursor: default; }
|
|
1535
1593
|
.dsh-model-menu-triggerLabel { text-overflow: ellipsis; white-space: nowrap; min-width: 0; overflow: hidden; }
|
|
1536
1594
|
.dsh-model-menu-triggerEffort { text-overflow: ellipsis; white-space: nowrap; min-width: 0; color: var(--dsw-alias-label-caption); flex-shrink: 1000; overflow: hidden; }
|
|
1537
|
-
.dsh-model-menu-chevron { color: var(--dsw-alias-label-caption); flex: none; transition: transform
|
|
1595
|
+
.dsh-model-menu-chevron { color: var(--dsw-alias-label-caption); flex: none; transition: transform var(--ds-transition-duration) var(--ds-ease-in-out); }
|
|
1538
1596
|
.dsh-model-menu-chevronOpen { transform: rotate(180deg); }
|
|
1597
|
+
/* Portal overlay: the host publishes no z-index token, so the card keeps the
|
|
1598
|
+
* native menu layer's 1100 verbatim. */
|
|
1539
1599
|
.dsh-model-menu-menu {
|
|
1540
1600
|
z-index: 1100;
|
|
1541
1601
|
--dsw-elevation-stroke-color: var(--dsw-alias-border-l1);
|
|
@@ -1561,6 +1621,8 @@ const modelMenuCss = `
|
|
|
1561
1621
|
.dsh-model-menu-searchWithQuery { padding-right: 34px; }
|
|
1562
1622
|
.dsh-model-menu-searchRow .dsh-model-menu-search input { padding: 0; font-size: 12px; line-height: normal; }
|
|
1563
1623
|
.dsh-model-menu-searchRow .dsh-model-menu-search input::placeholder { color: var(--dsw-alias-label-caption); }
|
|
1624
|
+
/* The clear button is a circle by geometry (border-radius: 50%), a shape the
|
|
1625
|
+
* radius token scale does not describe. */
|
|
1564
1626
|
.dsh-model-menu-searchClear {
|
|
1565
1627
|
width: 24px;
|
|
1566
1628
|
height: 24px;
|
|
@@ -2055,7 +2117,7 @@ function TitleSettings(props) {
|
|
|
2055
2117
|
});
|
|
2056
2118
|
}
|
|
2057
2119
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2058
|
-
className: "current-title-settings ct-root dsh-ui-
|
|
2120
|
+
className: "current-title-settings ct-root dsh-ui-page",
|
|
2059
2121
|
lang: seat.locale,
|
|
2060
2122
|
"data-testid": "current-title-settings",
|
|
2061
2123
|
children: [
|
|
@@ -2321,13 +2383,16 @@ function TitleSettings(props) {
|
|
|
2321
2383
|
]
|
|
2322
2384
|
});
|
|
2323
2385
|
}
|
|
2324
|
-
const
|
|
2325
|
-
.ct-root {
|
|
2326
|
-
.ct-prompt { min-height: 96px; }
|
|
2386
|
+
const currentTitleOwnCss = `
|
|
2387
|
+
.ct-root .ct-prompt { min-height: 96px; }
|
|
2327
2388
|
`;
|
|
2389
|
+
const currentTitleStyles = `${officialUiCss("ct-root")}${modelMenuCss}${currentTitleOwnCss}`;
|
|
2390
|
+
const styles = currentTitleStyles;
|
|
2328
2391
|
function apply(ctx) {
|
|
2329
2392
|
const client = ctx;
|
|
2330
|
-
const
|
|
2393
|
+
const packageName = "@klarkxy/dsh-current-title";
|
|
2394
|
+
mountModelSettingsChanges(ctx, packageName);
|
|
2395
|
+
const settingsSubscriber = createModelSettingsSubscriber(ctx, packageName, modelSettingsChangesIdentity(packageName));
|
|
2331
2396
|
const subscribeSettings = (change, error) => settingsSubscriber.subscribe(change, error);
|
|
2332
2397
|
const marker = createTitleClientMarker();
|
|
2333
2398
|
ctx.provide(TITLE_CLIENT_SERVICE, marker);
|
|
@@ -2367,6 +2432,8 @@ exports.TitleSettings = TitleSettings;
|
|
|
2367
2432
|
exports.apply = apply;
|
|
2368
2433
|
exports.applyMarkerFromStatus = applyMarkerFromStatus;
|
|
2369
2434
|
exports.createTitleClientMarker = createTitleClientMarker;
|
|
2435
|
+
exports.currentTitleOwnCss = currentTitleOwnCss;
|
|
2436
|
+
exports.currentTitleStyles = currentTitleStyles;
|
|
2370
2437
|
exports.disposeTitleClientMarker = disposeTitleClientMarker;
|
|
2371
2438
|
exports.inject = inject;
|
|
2372
2439
|
exports.name = name;
|
package/lib/contracts.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ModelRoute, defaultModelRoute, normalizeModelRoute } from "@klarkxy/dsh-model-route";
|
|
2
1
|
import { RpcResult } from "@klarkxy/dsh-plugin-kit/contracts";
|
|
2
|
+
import { ModelRoute, defaultModelRoute, normalizeModelRoute } from "@klarkxy/dsh-model-route";
|
|
3
3
|
//#region src/contracts.d.ts
|
|
4
4
|
declare const PLUGIN_NAME = "@klarkxy/dsh-current-title";
|
|
5
5
|
declare const FEATURE_ID = "current-title";
|
package/lib/index.d.ts
CHANGED
|
@@ -123,6 +123,7 @@ declare class CurrentTitleService {
|
|
|
123
123
|
private settingsPending;
|
|
124
124
|
private disposed;
|
|
125
125
|
private readonly inFlight;
|
|
126
|
+
private readonly generations;
|
|
126
127
|
readonly support: TitleSupport;
|
|
127
128
|
constructor(options: {
|
|
128
129
|
plugin: string;
|
|
@@ -155,6 +156,7 @@ declare class CurrentTitleService {
|
|
|
155
156
|
regenerate(sessionId: string, signal: AbortSignal): Promise<TitleSnapshot | undefined>;
|
|
156
157
|
read(session: SessionLike): TitleSnapshot | undefined;
|
|
157
158
|
private nativeGenerate;
|
|
159
|
+
private runNativeGenerate;
|
|
158
160
|
}
|
|
159
161
|
//#endregion
|
|
160
162
|
//#region src/input.d.ts
|
package/lib/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { DEFAULT_TIMEOUT_MS, PLUGIN_NAME, PROVIDER_ID, RPC_CHANNEL, SETTINGS_KEY, defaultGenerateConfig, defaultModelRoute, isTitleLocaleMode, normalizeCadence, normalizeModelRoute, normalizePrompt } from "./contracts.js";
|
|
2
2
|
import { registerHostRpc } from "@klarkxy/dsh-plugin-kit/host-rpc";
|
|
3
|
+
import { isSubagentSession, modelSettingsChangesIdentity } from "@klarkxy/dsh-plugin-kit/contracts";
|
|
4
|
+
import { createModelSettingsChanges } from "@klarkxy/dsh-plugin-kit/model-settings-changes";
|
|
3
5
|
import { notifyModelSettingsChanged, registerModelSettings } from "@klarkxy/dsh-plugin-kit/model-settings";
|
|
4
|
-
import "@klarkxy/dsh-model-route";
|
|
5
|
-
import { isSubagentSession } from "@klarkxy/dsh-plugin-kit/contracts";
|
|
6
6
|
import { callLlmText, resolveFeatureModel } from "@klarkxy/dsh-plugin-kit";
|
|
7
7
|
import { defineDomain, domainTable } from "@deepseek-ai/dsh-storage-domain";
|
|
8
8
|
import { z } from "zod";
|
|
@@ -251,32 +251,32 @@ async function generateCurrentTitle(options) {
|
|
|
251
251
|
const timeoutMs = Number.isFinite(options.config.timeoutMs) && options.config.timeoutMs > 0 ? options.config.timeoutMs : DEFAULT_TIMEOUT_MS;
|
|
252
252
|
const timer = setTimeout(() => deadline.abort(new DOMException("Title generation timed out.", "TimeoutError")), timeoutMs);
|
|
253
253
|
const signal = AbortSignal.any([options.signal, deadline.signal]);
|
|
254
|
-
let abort = () => {};
|
|
255
|
-
const cancelled = new Promise((_resolve, reject) => {
|
|
256
|
-
abort = () => reject(signal.reason);
|
|
257
|
-
if (signal.aborted) abort();
|
|
258
|
-
else signal.addEventListener("abort", abort, { once: true });
|
|
259
|
-
});
|
|
260
254
|
let result;
|
|
261
255
|
try {
|
|
262
|
-
result = await
|
|
256
|
+
result = await callLlmText(options.llm, {
|
|
263
257
|
plugin: PLUGIN_NAME,
|
|
264
258
|
route: callRoute,
|
|
265
259
|
system: systemPrompt(options.config, options.prompt),
|
|
266
260
|
text: framedInput,
|
|
267
261
|
maxTokens: options.config.maxOutputTokens,
|
|
268
262
|
purpose: "session-title",
|
|
263
|
+
configure: (controls, model) => {
|
|
264
|
+
const effort = model.reasoning?.efforts[0]?.id;
|
|
265
|
+
return {
|
|
266
|
+
...controls,
|
|
267
|
+
...effort === void 0 ? {} : { reasoningEffort: effort }
|
|
268
|
+
};
|
|
269
|
+
},
|
|
269
270
|
signal,
|
|
270
271
|
sessionId: options.sessionId,
|
|
271
272
|
isCurrent: options.isCurrent
|
|
272
|
-
})
|
|
273
|
+
});
|
|
273
274
|
signal.throwIfAborted();
|
|
274
275
|
} catch (error) {
|
|
275
276
|
signal.throwIfAborted();
|
|
276
277
|
throw error;
|
|
277
278
|
} finally {
|
|
278
279
|
clearTimeout(timer);
|
|
279
|
-
signal.removeEventListener("abort", abort);
|
|
280
280
|
}
|
|
281
281
|
options.signal.throwIfAborted();
|
|
282
282
|
if (!options.isCurrent()) throw Object.assign(/* @__PURE__ */ new Error("dsh-current-title: generation is no longer current"), { code: "superseded" });
|
|
@@ -487,6 +487,7 @@ var CurrentTitleService = class {
|
|
|
487
487
|
settingsPending = Promise.resolve();
|
|
488
488
|
disposed = false;
|
|
489
489
|
inFlight = /* @__PURE__ */ new Map();
|
|
490
|
+
generations = /* @__PURE__ */ new Set();
|
|
490
491
|
support;
|
|
491
492
|
constructor(options) {
|
|
492
493
|
this.options = options;
|
|
@@ -527,6 +528,7 @@ var CurrentTitleService = class {
|
|
|
527
528
|
this.generation += 1;
|
|
528
529
|
for (const controller of this.inFlight.values()) controller.abort(/* @__PURE__ */ new Error("current-title disabled"));
|
|
529
530
|
this.inFlight.clear();
|
|
531
|
+
await Promise.allSettled([...this.generations]);
|
|
530
532
|
await this.settingsPending;
|
|
531
533
|
const snapshot = this.nativeSlot?.displacement;
|
|
532
534
|
const outcome = await releaseTitleSlot(this.nativeSlot ?? {
|
|
@@ -634,7 +636,14 @@ var CurrentTitleService = class {
|
|
|
634
636
|
updatedAt: snapshot.updatedAt ?? 0
|
|
635
637
|
};
|
|
636
638
|
}
|
|
637
|
-
|
|
639
|
+
nativeGenerate(request) {
|
|
640
|
+
if (this.disposed) return Promise.reject(/* @__PURE__ */ new Error("dsh-current-title: plugin disabled"));
|
|
641
|
+
const pending = this.runNativeGenerate(request);
|
|
642
|
+
this.generations.add(pending);
|
|
643
|
+
pending.then(() => this.generations.delete(pending), () => this.generations.delete(pending));
|
|
644
|
+
return pending;
|
|
645
|
+
}
|
|
646
|
+
async runNativeGenerate(request) {
|
|
638
647
|
const llm = this.options.llm;
|
|
639
648
|
if (!llm) throw new Error("dsh-current-title: llm is not available");
|
|
640
649
|
const generation = this.generation;
|
|
@@ -801,6 +810,7 @@ const inject = [
|
|
|
801
810
|
"agentDefaultModel"
|
|
802
811
|
];
|
|
803
812
|
async function apply(ctx, config = {}) {
|
|
813
|
+
createModelSettingsChanges(ctx, modelSettingsChangesIdentity(PLUGIN_NAME), PLUGIN_NAME);
|
|
804
814
|
const host = ctx;
|
|
805
815
|
const domain = await host.storageDomain.open(titleDomain);
|
|
806
816
|
const table = domain.table("settings");
|