@eddyskywalker/dsh-chatgpt-subscription 0.39.2 → 0.40.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.
Files changed (34) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +34 -2
  3. package/lib/client.js +301 -0
  4. package/lib/client.js.map +1 -1
  5. package/lib/index.js +355 -10
  6. package/lib/types/client/CompactionRouteBundleConfig.d.ts +19 -0
  7. package/lib/types/client/CompactionRouteBundleConfig.d.ts.map +1 -0
  8. package/lib/types/client/CompactionRouteCard.d.ts +43 -0
  9. package/lib/types/client/CompactionRouteCard.d.ts.map +1 -0
  10. package/lib/types/client/api.d.ts +3 -1
  11. package/lib/types/client/api.d.ts.map +1 -1
  12. package/lib/types/client/index.d.ts.map +1 -1
  13. package/lib/types/client/locales-ru/hub.d.ts.map +1 -1
  14. package/lib/types/client/locales.d.ts +27 -1
  15. package/lib/types/client/locales.d.ts.map +1 -1
  16. package/lib/types/compat.d.ts +9 -1
  17. package/lib/types/compat.d.ts.map +1 -1
  18. package/lib/types/host/agent-preset.d.ts.map +1 -1
  19. package/lib/types/host/compaction-route/index.d.ts +132 -0
  20. package/lib/types/host/compaction-route/index.d.ts.map +1 -0
  21. package/lib/types/host/preferences.d.ts.map +1 -1
  22. package/lib/types/host/routes.d.ts.map +1 -1
  23. package/lib/types/host/subagent-model-authorization.d.ts +53 -1
  24. package/lib/types/host/subagent-model-authorization.d.ts.map +1 -1
  25. package/lib/types/index.d.ts.map +1 -1
  26. package/lib/types/shared/compaction-route.d.ts +63 -0
  27. package/lib/types/shared/compaction-route.d.ts.map +1 -0
  28. package/lib/types/shared/contracts.d.ts +26 -0
  29. package/lib/types/shared/contracts.d.ts.map +1 -1
  30. package/lib/types/shared/preferences.d.ts +2 -0
  31. package/lib/types/shared/preferences.d.ts.map +1 -1
  32. package/package.json +1 -1
  33. package/presets/dispatch/README.md +8 -2
  34. package/presets/dispatch/agent.cordis.yml +11 -4
package/lib/index.js CHANGED
@@ -133,7 +133,15 @@ const OAUTH_DEVICE_GRANT_LIFETIME_MS = 15 * 6e4;
133
133
  const OAUTH_DEVICE_MIN_POLL_INTERVAL_MS = 1e3;
134
134
  const TOKEN_REFRESH_MARGIN_MS = 6e4;
135
135
  const ROUTE_PREFIX$4 = "/api/dsh-chatgpt-subscription";
136
- const PLUGIN_VERSION = "0.1.0-alpha.0";
136
+ /**
137
+ * Version this line presents to the Codex backend in its `user-agent`.
138
+ *
139
+ * It must equal `package.json`'s version: a stale value here reports a plugin
140
+ * version that no release ever shipped, and nothing else in the build notices.
141
+ * `test/responses-client.test.ts` asserts the two agree, so a release that
142
+ * forgets this constant fails the suite instead of shipping quietly.
143
+ */
144
+ const PLUGIN_VERSION = "0.40.1";
137
145
  const CODEX_CHATGPT_PROVIDER_ID = "codex-chatgpt";
138
146
  const CODEX_API_BASE = "https://chatgpt.com/backend-api/codex";
139
147
  const CODEX_RESPONSES_URL = `${CODEX_API_BASE}/responses`;
@@ -10630,6 +10638,50 @@ var FilePreferencesStore = class {
10630
10638
  }
10631
10639
  };
10632
10640
  //#endregion
10641
+ //#region src/shared/compaction-route.ts
10642
+ /** Runtime membership test for one stored target. */
10643
+ function isCompactionRouteTarget(value) {
10644
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
10645
+ const record = value;
10646
+ return isNonEmptyString(record.provider) && isNonEmptyString(record.model);
10647
+ }
10648
+ function isNonEmptyString(value) {
10649
+ return typeof value === "string" && value.trim().length > 0;
10650
+ }
10651
+ /**
10652
+ * Decide whether the payload is admissible on the target.
10653
+ *
10654
+ * An undisclosed modality list is unknown, not negative: the harness documents
10655
+ * absence as "not disclosed", so refusing on it would disable the feature on
10656
+ * every route that simply does not advertise. The request still runs, and a
10657
+ * real rejection falls back the same way an error does.
10658
+ *
10659
+ * @param facts - what the directory says about the target.
10660
+ * @param needs - what the payload contains.
10661
+ * @returns the refusal reason, or `undefined` when the target may take it.
10662
+ */
10663
+ function compactionSkipReason(facts, needs) {
10664
+ const modalities = facts.modalities;
10665
+ if (modalities === void 0) return void 0;
10666
+ const accepts = new Set(modalities);
10667
+ if (needs.video && !accepts.has("video")) return "target-accepts-no-video";
10668
+ }
10669
+ /**
10670
+ * Whether the target can carry images inline.
10671
+ *
10672
+ * Used for the recorded outcome only: an image the target cannot take is
10673
+ * replaced by a placeholder during projection, so the summary is still produced
10674
+ * but may describe a screenshot it never saw.
10675
+ *
10676
+ * @param facts - what the directory says about the target.
10677
+ * @returns false only when the target discloses image support and omits it.
10678
+ */
10679
+ function compactionRoutesImages(facts) {
10680
+ const modalities = facts.modalities;
10681
+ if (modalities === void 0) return true;
10682
+ return modalities.includes("image");
10683
+ }
10684
+ //#endregion
10633
10685
  //#region src/shared/preferences.ts
10634
10686
  const PREFERENCES_NAMESPACE = "dsh-chatgpt-subscription";
10635
10687
  const DEFAULT_PREFERENCES = {
@@ -10642,7 +10694,8 @@ const DEFAULT_PREFERENCES = {
10642
10694
  searchProvider: "dsh",
10643
10695
  contextWindowOverrides: {},
10644
10696
  proxyMode: "auto",
10645
- customProxyUrl: null
10697
+ customProxyUrl: null,
10698
+ compactionRoute: null
10646
10699
  };
10647
10700
  const SEARCH_PROVIDER_CODEX = "codex";
10648
10701
  function isSearchProviderPreference(value) {
@@ -10848,7 +10901,11 @@ function registerPreferenceStore(settings) {
10848
10901
  z.const("custom"),
10849
10902
  z.const("direct")
10850
10903
  ]).default(DEFAULT_PREFERENCES.proxyMode),
10851
- customProxyUrl: z.union([z.string(), z.const(null)]).default(DEFAULT_PREFERENCES.customProxyUrl)
10904
+ customProxyUrl: z.union([z.string(), z.const(null)]).default(DEFAULT_PREFERENCES.customProxyUrl),
10905
+ compactionRoute: z.union([z.object({
10906
+ provider: z.string(),
10907
+ model: z.string()
10908
+ }), z.const(null)]).default(DEFAULT_PREFERENCES.compactionRoute ?? null)
10852
10909
  });
10853
10910
  if (!hasRegister(settings)) {
10854
10911
  const fileStore = new FilePreferencesStore(schema, preferencesPath(), readLegacyPreferences);
@@ -10899,6 +10956,13 @@ var SettingsPreferenceStore = class {
10899
10956
  if (!isProxyMode(patch.proxyMode)) throw new PreferenceError("Unsupported proxy mode preference.");
10900
10957
  normalized.proxyMode = patch.proxyMode;
10901
10958
  }
10959
+ if (patch.compactionRoute !== void 0) {
10960
+ if (patch.compactionRoute !== null && !isCompactionRouteTarget(patch.compactionRoute)) throw new PreferenceError("Unsupported compaction route.");
10961
+ normalized.compactionRoute = patch.compactionRoute === null ? null : {
10962
+ provider: patch.compactionRoute.provider.trim(),
10963
+ model: patch.compactionRoute.model.trim()
10964
+ };
10965
+ }
10902
10966
  if (patch.customProxyUrl !== void 0) if (patch.customProxyUrl !== null) {
10903
10967
  const trimmed = patch.customProxyUrl.trim();
10904
10968
  if (trimmed.length > 0 && !/^https?:\/\//i.test(trimmed) && !/^socks5?:\/\//i.test(trimmed)) normalized.customProxyUrl = `http://${trimmed}`;
@@ -13629,6 +13693,12 @@ function registerRoutes(ctx, oauth, usage, preferences, proxyManager, searchSwit
13629
13693
  value: await usage.testConnection()
13630
13694
  });
13631
13695
  return;
13696
+ case `${ROUTE_PREFIX$4}/compaction/candidates`:
13697
+ json(response, {
13698
+ ok: true,
13699
+ value: await compactionCandidates(ctx)
13700
+ });
13701
+ return;
13632
13702
  case `${ROUTE_PREFIX$4}/preferences/update`: {
13633
13703
  const patch = readPreferencesUpdate(body, preferences.status());
13634
13704
  const value = await preferences.update(patch);
@@ -13737,6 +13807,23 @@ function field(value, name) {
13737
13807
  const candidate = value[name];
13738
13808
  return typeof candidate === "string" && candidate !== "" ? candidate : null;
13739
13809
  }
13810
+ /** The provider and model directory a compaction target is chosen from. */
13811
+ async function compactionCandidates(ctx) {
13812
+ const llm = ctx.llm;
13813
+ if (llm === void 0 || typeof llm.listProviders !== "function") return { providers: [] };
13814
+ return { providers: (await Promise.all(llm.listProviders().map(async (provider) => {
13815
+ const models = await llm.listModels(provider.id).catch(() => []);
13816
+ return {
13817
+ id: provider.id,
13818
+ name: provider.name,
13819
+ models: models.map((model) => ({
13820
+ id: model.id,
13821
+ name: model.name,
13822
+ modalities: [...model.inputModalities ?? []]
13823
+ }))
13824
+ };
13825
+ }))).filter((provider) => provider.models.length > 0) };
13826
+ }
13740
13827
  function isRecord$44(value) {
13741
13828
  return typeof value === "object" && value !== null && !Array.isArray(value);
13742
13829
  }
@@ -13770,6 +13857,13 @@ function readPreferencesUpdate(value, current) {
13770
13857
  if (value.searchProvider !== "dsh" && value.searchProvider !== "codex") throw new PreferenceError("searchProvider must be dsh or codex.");
13771
13858
  patch.searchProvider = value.searchProvider;
13772
13859
  }
13860
+ if ("compactionRoute" in value) {
13861
+ if (value.compactionRoute !== null && !isCompactionRouteTarget(value.compactionRoute)) throw new PreferenceError("compactionRoute must be null or an object with provider and model.");
13862
+ patch.compactionRoute = value.compactionRoute === null ? null : {
13863
+ provider: value.compactionRoute.provider.trim(),
13864
+ model: value.compactionRoute.model.trim()
13865
+ };
13866
+ }
13773
13867
  if ("contextWindowOverrides" in value) {
13774
13868
  if (!isRecord$44(value.contextWindowOverrides)) throw new PreferenceError("contextWindowOverrides must be an object.");
13775
13869
  const overrides = {};
@@ -75608,6 +75702,34 @@ function inheritRouteDenialReason(toolName, provider, model, allowed, reasoningE
75608
75702
  return `subagent model selection: "${toolName}" keeps its child on ${provider === void 0 || model === void 0 ? "the calling agent's route" : `"${provider}/${model}"${reasoningEffort === void 0 ? "" : ` (${reasoningEffort})`}`}, which this Session's allowlist does not authorize, so it is unavailable for now. Its child cannot be re-routed, so either delegate through a tool that exposes provider and model — passing one of ${authorizedRoutesText(allowed)} — or continue on an authorized model for this session, after which ${toolName} works again.`;
75609
75703
  }
75610
75704
  /**
75705
+ * Enforce the allowlist against the route an inherit-mode child would run on.
75706
+ * @param agent - Calling agent whose route the child inherits.
75707
+ * @param toolName - the tool the model called.
75708
+ * @param allowed - Routes the calling Session authorizes.
75709
+ * @returns a denial reason when the inherited route is unauthorized.
75710
+ */
75711
+ function inheritedRouteDenial(agent, toolName, allowed) {
75712
+ const inherited = inheritedRouteOf(agent);
75713
+ if (inherited.provider !== void 0 && inherited.model !== void 0 && routesInclude(allowed, inherited.provider, inherited.model)) return void 0;
75714
+ return inheritRouteDenialReason(toolName, inherited.provider, inherited.model, allowed, inherited.reasoningEffort);
75715
+ }
75716
+ /**
75717
+ * Build the denial reason for a half-specified route aimed at a tool that
75718
+ * exposes no route field at all.
75719
+ *
75720
+ * This is the one denial whose cause is not the call: the delegation tool was
75721
+ * mounted without model selection, so no argument can name a child route. The
75722
+ * reason states the configuration defect and the route the child would inherit,
75723
+ * because "call list_subagent_models and pass a route" would be unactionable.
75724
+ * @param toolName - the tool the model called.
75725
+ * @param inherited - the route the child would inherit from the caller.
75726
+ * @param allowed - Routes the calling Session authorizes.
75727
+ * @returns the corrective reason handed back to the model.
75728
+ */
75729
+ function routeFieldsUnavailableReason(toolName, inherited, allowed) {
75730
+ return `subagent model selection: "${toolName}" exposes no provider/model parameter in this deployment, so no call can name a child route — the delegation tool is mounted without model selection enabled. The child would run on ${inherited.provider === void 0 || inherited.model === void 0 ? "the calling agent's own route" : `"${inherited.provider}/${inherited.model}"`}; authorized routes are ${authorizedRoutesText(allowed)}.`;
75731
+ }
75732
+ /**
75611
75733
  * Resolve one tool's enforcement mode from the configured policies. An
75612
75734
  * unconfigured name is `undefined`: the guard does not police it, which is how
75613
75735
  * the plain-name configuration keeps its existing meaning.
@@ -75773,7 +75895,7 @@ function rewriteAuthorizedPromptAssembly(assembly, allowed, toolNames = DEFAULT_
75773
75895
  * @param inheritToolNames - Delegation tools that start a child on the caller's own route.
75774
75896
  * @returns a denial reason, or undefined to leave the call untouched.
75775
75897
  */
75776
- function delegationDenialReason(agent, toolName, args, preference, sessions, toolNames = DEFAULT_DELEGATION_TOOLS, scope = "session", inheritToolNames = []) {
75898
+ function delegationDenialReason(agent, toolName, args, preference, sessions, toolNames = DEFAULT_DELEGATION_TOOLS, scope = "session", inheritToolNames = [], declaresRouteParameters) {
75777
75899
  const mode = delegationModeOf([...toolNames.map((name) => ({
75778
75900
  name,
75779
75901
  mode: "explicit"
@@ -75789,9 +75911,11 @@ function delegationDenialReason(agent, toolName, args, preference, sessions, too
75789
75911
  const requestedModel = asString$1(request["model"]);
75790
75912
  if (mode === "inherit") {
75791
75913
  if (requestedProvider !== void 0 && requestedModel !== void 0) return inheritOverrideReason(toolName, requestedProvider, requestedModel);
75792
- const inherited = inheritedRouteOf(agent);
75793
- if (inherited.provider !== void 0 && inherited.model !== void 0 && routesInclude(allowed, inherited.provider, inherited.model)) return void 0;
75794
- return inheritRouteDenialReason(toolName, inherited.provider, inherited.model, allowed, inherited.reasoningEffort);
75914
+ return inheritedRouteDenial(agent, toolName, allowed);
75915
+ }
75916
+ if (declaresRouteParameters?.(agent, toolName) === false) {
75917
+ if (requestedProvider === void 0 && requestedModel === void 0) return inheritedRouteDenial(agent, toolName, allowed);
75918
+ if (requestedProvider === void 0 || requestedModel === void 0) return routeFieldsUnavailableReason(toolName, inheritedRouteOf(agent), allowed);
75795
75919
  }
75796
75920
  if (requestedProvider === void 0) return requestedModel === void 0 ? missingRouteReason(allowed) : partialRouteReason("provider", requestedModel, allowed);
75797
75921
  if (requestedModel === void 0) return partialRouteReason("model", requestedProvider, allowed);
@@ -75808,7 +75932,7 @@ function delegationDenialReason(agent, toolName, args, preference, sessions, too
75808
75932
  * @returns a guard that denies delegations outside the Session allowlist.
75809
75933
  */
75810
75934
  function createSubagentAuthorization(options) {
75811
- return (agent, toolName, args) => delegationDenialReason(agent, toolName, args, subagentModelSelectionPreference(options.settings), options.sessions, options.toolNames, options.scope ?? "session", options.inheritToolNames ?? []);
75935
+ return (agent, toolName, args) => delegationDenialReason(agent, toolName, args, subagentModelSelectionPreference(options.settings), options.sessions, options.toolNames, options.scope ?? "session", options.inheritToolNames ?? [], options.declaresRouteParameters);
75812
75936
  }
75813
75937
  /** Delegation names this guard recognizes when configuration omits them. */
75814
75938
  function normalizeDelegationToolNames(value) {
@@ -75849,6 +75973,36 @@ function validateDelegationToolNames(toolNames) {
75849
75973
  return [...toolNames];
75850
75974
  }
75851
75975
  /**
75976
+ * Build the live route-parameter probe from the Host tool registry.
75977
+ *
75978
+ * The registry is read per call rather than snapshotted: an HMR replacement or a
75979
+ * preset change can remount the delegation tool, and a stale answer would put
75980
+ * the guard back on the impossible branch.
75981
+ * @param ctx - Host context carrying the `tools` registry.
75982
+ * @returns a probe over the registry, or undefined when it exposes no readable
75983
+ * definitions (older harness generations), which keeps the configured mode.
75984
+ */
75985
+ function routeParameterProbe(ctx) {
75986
+ const registry = ctx.tools;
75987
+ if (typeof registry?.definitions !== "function") return void 0;
75988
+ const definitions = registry.definitions.bind(registry);
75989
+ return (agent, toolName) => {
75990
+ let visible;
75991
+ try {
75992
+ visible = definitions(agent);
75993
+ } catch {
75994
+ return;
75995
+ }
75996
+ const definition = visible.find((entry) => entry?.name === toolName);
75997
+ if (definition === void 0) return void 0;
75998
+ const parameters = asRecord$1(definition.parameters);
75999
+ if (parameters === void 0) return void 0;
76000
+ const properties = asRecord$1(parameters["properties"]);
76001
+ if (properties !== void 0) return "provider" in properties || "model" in properties;
76002
+ return "provider" in parameters || "model" in parameters;
76003
+ };
76004
+ }
76005
+ /**
75852
76006
  * Register the monotonic guard on the Host tool registry, and — when the Host
75853
76007
  * context exposes Cordis events — enrich `list_subagent_models` output with the
75854
76008
  * Session's split `{ provider, model }` routes and rewrite the misleading
@@ -75869,7 +76023,8 @@ function installSubagentModelAuthorization(ctx, sessions, config = {}) {
75869
76023
  sessions,
75870
76024
  toolNames,
75871
76025
  inheritToolNames,
75872
- scope
76026
+ scope,
76027
+ declaresRouteParameters: routeParameterProbe(ctx)
75873
76028
  });
75874
76029
  const disposeGuard = ctx.tools.guard((exec) => authorize(exec.agent, exec.name, exec.arguments));
75875
76030
  const eventHost = ctx;
@@ -76511,6 +76666,7 @@ Before dispatching, check whether subagent model selection is enabled in this se
76511
76666
 
76512
76667
  subagent_fork deliberately shares the parent agent's route (reusing conversation history and KV cache); use it only for reviews that require the coordinator's context, never as a shortcut around explicit routing.
76513
76668
  It has no provider/model parameters, so the explicit-route rule under [State: Listed] does not apply to it; however, the guard still validates the route it inherits: if your current model is not in the allowlist, subagent_fork will be hard-rejected—switch to subagent with an explicit allowed route, or switch this session to an allowed model so subagent_fork becomes available again.
76669
+ When the parent route is itself one of the authorized routes, subagent_fork needs no route argument and no discovery tool: its admission is decided by the route the calling session is actually running, so it still works when list_subagent_models is absent or when a delegation tool exposes no route field. Prefer it exactly then—parent route authorized and the subtask needs this conversation's context—and prefer subagent when the subtask needs another model or a clean context. Never conclude that delegation is impossible from a broken explicit-route path alone: try subagent_fork once and read the denial, which names the parent route and the authorized routes.
76514
76670
  Choose models based on task nature and model capability; keep model selection consistent across tasks of the same kind within a batch.
76515
76671
 
76516
76672
  ## R3 · Subagent Autonomy
@@ -76698,7 +76854,8 @@ Reset: fallback applies only to the current task. When the next task begins, alw
76698
76854
  config: {
76699
76855
  provider: "spawn",
76700
76856
  toolName: "subagent",
76701
- modelSelectionSettings: true,
76857
+ modelSelection: true,
76858
+ modelDiscovery: true,
76702
76859
  backgroundMode: "continuable"
76703
76860
  }
76704
76861
  },
@@ -78004,6 +78161,178 @@ function releaseListener$1(handle) {
78004
78161
  }
78005
78162
  handle?.dispose?.();
78006
78163
  }
78164
+ //#endregion
78165
+ //#region src/host/compaction-route/index.ts
78166
+ /**
78167
+ * Route an automatic compaction summary to a model other than the one that
78168
+ * built the conversation.
78169
+ *
78170
+ * A host computes the summary on the conversation's own model unless its
78171
+ * compaction config names another route. That config is preset-owned, so this
78172
+ * plugin cannot offer it as a setting; it re-routes the call one layer down,
78173
+ * at the `llm/stream` waterfall, where a compaction request is identifiable by
78174
+ * its `purpose` and every preset passes through.
78175
+ *
78176
+ * Three rules shape the listener:
78177
+ *
78178
+ * - **Refuse before sending what cannot work.** A video block has no wire
78179
+ * fallback on a target that does not take video, so the request would fail
78180
+ * after paying for it. The payload is inspected first and the conversation
78181
+ * model keeps the call instead.
78182
+ * - **Buffer, then fall back.** A host turns an adapter throw into a terminal
78183
+ * `finish` chunk instead of an exception, and a half-written summary is
78184
+ * worthless, so the alternate attempt is collected first. Only a complete one
78185
+ * is released downstream; anything else falls back to the conversation model
78186
+ * and is recorded, because a fallback the operator cannot see is a cost they
78187
+ * did not agree to.
78188
+ * - **Do not impose an output cap.** The host asks for 65 536 tokens of summary
78189
+ * by default; the official client sends no cap at all and lets the route
78190
+ * decide, so the cap is dropped for the re-routed call.
78191
+ *
78192
+ * @module dsh-chatgpt-subscription/compaction-route
78193
+ */
78194
+ /** How long one provider's model directory is reused. */
78195
+ const MODEL_CACHE_MS = 6e4;
78196
+ /**
78197
+ * Install the `llm/stream` listener.
78198
+ *
78199
+ * Returns `undefined` when the host exposes no event bus, so a caller can skip
78200
+ * installation instead of failing the plugin's load.
78201
+ *
78202
+ * @param ctx - structural context: the event bus and a logger.
78203
+ * @param deps - the LLM stream, the live target, and the optional directory.
78204
+ * @returns the live handle, or `undefined` on a host without an event bus.
78205
+ */
78206
+ function installCompactionRoute(ctx, deps) {
78207
+ const on = ctx.on;
78208
+ if (typeof on !== "function") return void 0;
78209
+ let outcome = null;
78210
+ const rerouted = /* @__PURE__ */ new WeakSet();
78211
+ const directory = /* @__PURE__ */ new Map();
78212
+ const factsFor = async (target) => {
78213
+ if (deps.listModels === void 0) return {};
78214
+ const now = Date.now();
78215
+ const cached = directory.get(target.provider);
78216
+ const models = cached !== void 0 && now - cached.at < MODEL_CACHE_MS ? cached.models : await deps.listModels(target.provider).catch(() => []);
78217
+ if (cached === void 0 || now - cached.at >= MODEL_CACHE_MS) directory.set(target.provider, {
78218
+ at: now,
78219
+ models
78220
+ });
78221
+ const entry = models.find((model) => model.id === target.model);
78222
+ return entry === void 0 ? {} : { modalities: entry.inputModalities };
78223
+ };
78224
+ const dispose = on("llm/stream", async function* routeByStream(options, next) {
78225
+ if (options.purpose !== "compaction" || rerouted.has(options)) return yield* next();
78226
+ const target = deps.readTarget();
78227
+ if (target === null) return yield* next();
78228
+ if (target.provider === options.provider && target.model === options.model) return yield* next();
78229
+ const facts = await factsFor(target);
78230
+ const needs = payloadNeeds(options);
78231
+ const skip = compactionSkipReason(facts, needs);
78232
+ if (skip !== void 0) {
78233
+ outcome = {
78234
+ kind: "skipped",
78235
+ at: Date.now(),
78236
+ reason: skip
78237
+ };
78238
+ ctx.logger?.warn(`[dsh-chatgpt-subscription] compaction kept on ${String(options.provider)}/${String(options.model)}: ${target.provider}/${target.model} ${skip}`);
78239
+ return yield* next();
78240
+ }
78241
+ const reroutedOptions = withoutOutputCap(options, target);
78242
+ rerouted.add(reroutedOptions);
78243
+ const attempt = await collect(() => deps.stream(reroutedOptions));
78244
+ if (attempt.ok) {
78245
+ outcome = {
78246
+ kind: "routed",
78247
+ provider: target.provider,
78248
+ model: target.model,
78249
+ at: Date.now(),
78250
+ imagesProjected: needs.image && !compactionRoutesImages(facts)
78251
+ };
78252
+ for (const chunk of attempt.chunks) yield chunk;
78253
+ return;
78254
+ }
78255
+ outcome = {
78256
+ kind: "fell-back",
78257
+ provider: options.provider ?? "",
78258
+ model: options.model ?? "",
78259
+ at: Date.now(),
78260
+ reason: attempt.reason
78261
+ };
78262
+ ctx.logger?.warn(`[dsh-chatgpt-subscription] compaction fell back to ${String(options.provider)}/${String(options.model)}: ${target.provider}/${target.model} failed with ${attempt.reason}`);
78263
+ return yield* next();
78264
+ }, { global: true });
78265
+ return {
78266
+ status: () => outcome,
78267
+ dispose: () => {
78268
+ if (typeof dispose === "function") dispose();
78269
+ }
78270
+ };
78271
+ }
78272
+ /**
78273
+ * Read what the payload actually carries.
78274
+ * @param options - the compaction request.
78275
+ * @returns whether it carries an image or a video block.
78276
+ */
78277
+ function payloadNeeds(options) {
78278
+ let image = false;
78279
+ let video = false;
78280
+ for (const message of options.messages ?? []) for (const block of message.content ?? []) if (block.type === "image") image = true;
78281
+ else if (block.type === "video") video = true;
78282
+ return {
78283
+ image,
78284
+ video
78285
+ };
78286
+ }
78287
+ /**
78288
+ * Build the request for the chosen target: the same conversation, no output cap.
78289
+ * @param options - the original compaction request.
78290
+ * @param target - the route to send it to.
78291
+ * @returns a detached request object.
78292
+ */
78293
+ function withoutOutputCap(options, target) {
78294
+ const { maxTokens: _dropped, ...rest } = options;
78295
+ return {
78296
+ ...rest,
78297
+ provider: target.provider,
78298
+ model: target.model
78299
+ };
78300
+ }
78301
+ /**
78302
+ * Run one attempt to completion, keeping its chunks only when it finished
78303
+ * cleanly.
78304
+ *
78305
+ * The stream is opened inside the try, not before it: an adapter that rejects
78306
+ * before yielding anything (an exhausted quota, a rejected credential) throws
78307
+ * from the call itself, and that is the same fallback case as a failed stream.
78308
+ */
78309
+ async function collect(open) {
78310
+ const chunks = [];
78311
+ let finishKind;
78312
+ try {
78313
+ for await (const chunk of open()) {
78314
+ chunks.push(chunk);
78315
+ if (chunk.type === "finish") finishKind = chunk.reason?.kind;
78316
+ }
78317
+ } catch (error) {
78318
+ return {
78319
+ ok: false,
78320
+ reason: error instanceof Error ? error.message : String(error)
78321
+ };
78322
+ }
78323
+ if (finishKind === void 0) return {
78324
+ ok: false,
78325
+ reason: "the stream ended without a finish chunk"
78326
+ };
78327
+ if (finishKind === "error" || finishKind === "aborted") return {
78328
+ ok: false,
78329
+ reason: finishKind
78330
+ };
78331
+ return {
78332
+ ok: true,
78333
+ chunks
78334
+ };
78335
+ }
78007
78336
  const DEFAULT_NO_ANSWER_RESUME_OPTIONS = {
78008
78337
  maxResumes: 2,
78009
78338
  text: "Your previous response ended without an answer and without calling a tool. Continue from where you left off: either state the conclusion you already reached, or make the single next tool call that moves the task forward. Do not restart the analysis."
@@ -79243,6 +79572,22 @@ function apply(ctx, pluginConfig = {}) {
79243
79572
  const store = createPlatformTokenStore();
79244
79573
  const preferences = registerPreferenceStore(ctx.settings);
79245
79574
  preferences.hydrate().catch(() => void 0);
79575
+ ctx.effect(() => {
79576
+ const llm = ctx.llm;
79577
+ const handle = installCompactionRoute({
79578
+ on: (event, listener, options) => ctx.on?.(event, listener, options),
79579
+ logger: ctx.logger
79580
+ }, {
79581
+ stream: (options) => llm.stream(options),
79582
+ listModels: (provider) => llm.listModels(provider),
79583
+ readTarget: () => preferences.status().compactionRoute ?? null
79584
+ });
79585
+ if (handle === void 0) {
79586
+ ctx.logger.warn("[dsh-chatgpt-subscription] compaction route skipped: this harness exposes no event bus");
79587
+ return () => void 0;
79588
+ }
79589
+ return () => handle.dispose();
79590
+ }, "dsh-chatgpt-subscription: compaction route");
79246
79591
  const antigravityStore = new FileCredentialStore$3();
79247
79592
  const antigravityAccountPool = new AccountPoolStore(void 0, void 0, antigravityStore);
79248
79593
  const antigravityModelSettings = new FileModelSettingsStore$2();
@@ -0,0 +1,19 @@
1
+ import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
2
+ import { NS } from './locales.ts';
3
+ /** The package whose page this entry configures. */
4
+ export declare const COMPACTION_ROUTE_PACKAGE = "@eddyskywalker/dsh-chatgpt-subscription";
5
+ /** The view the page asks a configuration entry for. */
6
+ export interface PluginConfigViewLike {
7
+ readonly view: 'summary' | 'page';
8
+ }
9
+ /** Props the plugin-manager bundle page renders a configuration entry with. */
10
+ export type CompactionRouteBundleConfigProps = PropsLocale<typeof NS> & PluginConfigViewLike;
11
+ /**
12
+ * Render the compaction-model card on this package's bundle page only.
13
+ *
14
+ * Split in two so the hooks below never mount for the summary view: that view
15
+ * is a one-liner drawn beside the card, and fetching this plugin's preferences
16
+ * for it would be a request nobody asked for.
17
+ */
18
+ export declare function CompactionRouteBundleConfig(props: CompactionRouteBundleConfigProps): React.JSX.Element | null;
19
+ //# sourceMappingURL=CompactionRouteBundleConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CompactionRouteBundleConfig.d.ts","sourceRoot":"","sources":["../../../src/client/CompactionRouteBundleConfig.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAKjC,oDAAoD;AACpD,eAAO,MAAM,wBAAwB,4CAA4C,CAAA;AAEjF,wDAAwD;AACxD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAAA;CAClC;AAED,+EAA+E;AAC/E,MAAM,MAAM,gCAAgC,GACxC,WAAW,CAAC,OAAO,EAAE,CAAC,GACtB,oBAAoB,CAAA;AAExB;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,gCAAgC,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CAG7G"}
@@ -0,0 +1,43 @@
1
+ import type { SubscriptionPreferencesDto } from '../shared/contracts.ts';
2
+ import type { SubscriptionApi } from './api.ts';
3
+ /** Labels the settings page supplies, so this card carries no locale of its own. */
4
+ export interface CompactionRouteCardLabels {
5
+ title: string;
6
+ hint: string;
7
+ provider: string;
8
+ model: string;
9
+ save: string;
10
+ clear: string;
11
+ change: string;
12
+ /** Shown as the current value when no route is configured. */
13
+ follow: string;
14
+ saved: string;
15
+ failed: string;
16
+ /** Shown when the chosen model cannot take video, so compaction would fall back. */
17
+ noVideo: string;
18
+ /** Shown when the chosen model cannot take images, so they become placeholders. */
19
+ noImage: string;
20
+ empty: string;
21
+ }
22
+ export interface CompactionRouteCardProps {
23
+ readonly api: SubscriptionApi;
24
+ readonly preferences: SubscriptionPreferencesDto | null;
25
+ readonly disabled?: boolean;
26
+ readonly labels: CompactionRouteCardLabels;
27
+ readonly onSaved: (preferences: SubscriptionPreferencesDto) => void;
28
+ }
29
+ /**
30
+ * Choose the model an automatic compaction summarizes on.
31
+ *
32
+ * The route lives in this plugin's preferences and is applied by the host's
33
+ * `llm/stream` listener, so a saved choice takes effect on the next compaction
34
+ * without a restart. Both selects are needed together: a model id alone belongs
35
+ * to no route.
36
+ *
37
+ * The picker loads its directory on demand rather than on mount: opening the
38
+ * settings page must not cost a request to every line (that page is opened for
39
+ * unrelated reasons all the time), and the card already knows the saved route
40
+ * from the status it was rendered with.
41
+ */
42
+ export declare function CompactionRouteCard({ api, preferences, disabled, labels, onSaved }: CompactionRouteCardProps): React.JSX.Element;
43
+ //# sourceMappingURL=CompactionRouteCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CompactionRouteCard.d.ts","sourceRoot":"","sources":["../../../src/client/CompactionRouteCard.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,0BAA0B,EAC3B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE/C,oFAAoF;AACpF,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,oFAAoF;IACpF,OAAO,EAAE,MAAM,CAAA;IACf,mFAAmF;IACnF,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAA;IAC7B,QAAQ,CAAC,WAAW,EAAE,0BAA0B,GAAG,IAAI,CAAA;IACvD,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAC3B,QAAQ,CAAC,MAAM,EAAE,yBAAyB,CAAA;IAC1C,QAAQ,CAAC,OAAO,EAAE,CAAC,WAAW,EAAE,0BAA0B,KAAK,IAAI,CAAA;CACpE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,QAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,wBAAwB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAsIxI"}
@@ -1,4 +1,4 @@
1
- import type { DeviceLoginStartDto, LoginEventDto, LoginStartDto, PluginStatusDto, QuotaStatusDto, ConnectionTestDto, SubscriptionPreferencesDto, SubscriptionPreferencesUpdateDto } from '../shared/contracts.ts';
1
+ import type { DeviceLoginStartDto, LoginEventDto, LoginStartDto, PluginStatusDto, QuotaStatusDto, ConnectionTestDto, SubscriptionPreferencesDto, SubscriptionPreferencesUpdateDto, CompactionCandidatesDto } from '../shared/contracts.ts';
2
2
  export declare class SubscriptionApi {
3
3
  status(): Promise<PluginStatusDto>;
4
4
  startLogin(): Promise<LoginStartDto>;
@@ -52,6 +52,8 @@ export declare class SubscriptionApi {
52
52
  useResetCredit(): Promise<QuotaStatusDto>;
53
53
  testConnection(): Promise<ConnectionTestDto>;
54
54
  updatePreferences(patch: SubscriptionPreferencesUpdateDto): Promise<SubscriptionPreferencesDto>;
55
+ /** Every route a compaction summary may be pointed at, read per call. */
56
+ compactionCandidates(): Promise<CompactionCandidatesDto>;
55
57
  events(loginId: string): EventSource;
56
58
  }
57
59
  export declare function parseLoginEvent(event: MessageEvent<string>): LoginEventDto | null;
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/client/api.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,0BAA0B,EAC1B,gCAAgC,EACjC,MAAM,wBAAwB,CAAA;AAE/B,qBAAa,eAAe;IAC1B,MAAM,IAAI,OAAO,CAAC,eAAe,CAAC;IAIlC,UAAU,IAAI,OAAO,CAAC,aAAa,CAAC;IAIpC;;;;;;OAMG;IACH,gBAAgB,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAIhD,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IAI7D,kEAAkE;IAClE,MAAM,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,aAAa,EAAE,KAAK,CAAA;KAAE,CAAC;IAI7D;;;;;OAKG;IACH,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,WAAW,GAAG,QAAQ,GAAG,gBAAgB,GAAG,UAAU,GAAG,SAAS,EAAE,IAAI,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,eAAe,CAAC;IAIzM;;;;;;;;OAQG;IACH,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC;IAI1D,6EAA6E;IAC7E,yBAAyB,IAAI,OAAO,CAAC,eAAe,CAAC;IAIrD;;;;;OAKG;IACH,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,aAAa,GAAG,OAAO,CAAC,eAAe,CAAC;IAIzE,OAAO,IAAI,OAAO,CAAC,eAAe,CAAC;IAInC,YAAY,IAAI,OAAO,CAAC,cAAc,CAAC;IAIvC,cAAc,IAAI,OAAO,CAAC,cAAc,CAAC;IAIzC,cAAc,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAI5C,iBAAiB,CAAC,KAAK,EAAE,gCAAgC,GAAG,OAAO,CAAC,0BAA0B,CAAC;IAI/F,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW;CAGrC;AAmBD,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG,aAAa,GAAG,IAAI,CAOjF"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/client/api.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,0BAA0B,EAC1B,gCAAgC,EAChC,uBAAuB,EACxB,MAAM,wBAAwB,CAAA;AAE/B,qBAAa,eAAe;IAC1B,MAAM,IAAI,OAAO,CAAC,eAAe,CAAC;IAIlC,UAAU,IAAI,OAAO,CAAC,aAAa,CAAC;IAIpC;;;;;;OAMG;IACH,gBAAgB,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAIhD,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IAI7D,kEAAkE;IAClE,MAAM,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,aAAa,EAAE,KAAK,CAAA;KAAE,CAAC;IAI7D;;;;;OAKG;IACH,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,WAAW,GAAG,QAAQ,GAAG,gBAAgB,GAAG,UAAU,GAAG,SAAS,EAAE,IAAI,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,eAAe,CAAC;IAIzM;;;;;;;;OAQG;IACH,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,CAAC;IAI1D,6EAA6E;IAC7E,yBAAyB,IAAI,OAAO,CAAC,eAAe,CAAC;IAIrD;;;;;OAKG;IACH,cAAc,CAAC,IAAI,EAAE,SAAS,GAAG,aAAa,GAAG,OAAO,CAAC,eAAe,CAAC;IAIzE,OAAO,IAAI,OAAO,CAAC,eAAe,CAAC;IAInC,YAAY,IAAI,OAAO,CAAC,cAAc,CAAC;IAIvC,cAAc,IAAI,OAAO,CAAC,cAAc,CAAC;IAIzC,cAAc,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAI5C,iBAAiB,CAAC,KAAK,EAAE,gCAAgC,GAAG,OAAO,CAAC,0BAA0B,CAAC;IAI/F,yEAAyE;IACzE,oBAAoB,IAAI,OAAO,CAAC,uBAAuB,CAAC;IAIxD,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW;CAGrC;AAmBD,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG,aAAa,GAAG,IAAI,CAOjF"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAenE,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AAQ/D,OAAO,EAAoD,KAAK,oBAAoB,EAAE,MAAM,0BAA0B,CAAA;AAGtH,OAAO,EAA0C,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAGlG,OAAO,EAAqD,KAAK,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAExH,OAAO,EAA+C,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAM5G,OAAO,EAAqD,KAAK,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAKxH,OAAO,EAA0D,KAAK,sBAAsB,EAAE,MAAM,8BAA8B,CAAA;AAElI,OAAO,EAA0C,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAElG,OAAO,EAA+C,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAG5G,OAAO,EAAiD,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAGhH,OAAO,EAAwC,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAG9F,OAAO,EAAkD,KAAK,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAMlH,OAAO,EAA6D,KAAK,wBAAwB,EAAE,MAAM,+BAA+B,CAAA;AAExI,OAAO,QAAQ,kCAAkC,CAAC;IAChD;;;;;;;OAOG;IACH,UAAU,kBAAkB;QAC1B,0BAA0B,EAAE,SAAS,CAAA;QACrC,iBAAiB,EAAE,oBAAoB,CAAA;QACvC,YAAY,EAAE,eAAe,CAAA;QAC7B,kBAAkB,EAAE,oBAAoB,CAAA;QACxC,eAAe,EAAE,iBAAiB,CAAA;QAClC,kBAAkB,EAAE,oBAAoB,CAAA;QACxC,qBAAqB,EAAE,sBAAsB,CAAA;QAC7C,YAAY,EAAE,eAAe,CAAA;QAC7B,eAAe,EAAE,iBAAiB,CAAA;QAClC,gBAAgB,EAAE,kBAAkB,CAAA;QACpC,WAAW,EAAE,cAAc,CAAA;QAC3B,gBAAgB,EAAE,mBAAmB,CAAA;QACrC,sBAAsB,EAAE,wBAAwB,CAAA;KACjD;CACF;AAED,eAAO,MAAM,MAAM,UAQlB,CAAA;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CA2N9C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAgBnE,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AAQ/D,OAAO,EAAoD,KAAK,oBAAoB,EAAE,MAAM,0BAA0B,CAAA;AAGtH,OAAO,EAA0C,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAGlG,OAAO,EAAqD,KAAK,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAExH,OAAO,EAA+C,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAM5G,OAAO,EAAqD,KAAK,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAKxH,OAAO,EAA0D,KAAK,sBAAsB,EAAE,MAAM,8BAA8B,CAAA;AAElI,OAAO,EAA0C,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAElG,OAAO,EAA+C,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAG5G,OAAO,EAAiD,KAAK,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAGhH,OAAO,EAAwC,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAG9F,OAAO,EAAkD,KAAK,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAMlH,OAAO,EAA6D,KAAK,wBAAwB,EAAE,MAAM,+BAA+B,CAAA;AAExI,OAAO,QAAQ,kCAAkC,CAAC;IAChD;;;;;;;OAOG;IACH,UAAU,kBAAkB;QAC1B,0BAA0B,EAAE,SAAS,CAAA;QACrC,iBAAiB,EAAE,oBAAoB,CAAA;QACvC,YAAY,EAAE,eAAe,CAAA;QAC7B,kBAAkB,EAAE,oBAAoB,CAAA;QACxC,eAAe,EAAE,iBAAiB,CAAA;QAClC,kBAAkB,EAAE,oBAAoB,CAAA;QACxC,qBAAqB,EAAE,sBAAsB,CAAA;QAC7C,YAAY,EAAE,eAAe,CAAA;QAC7B,eAAe,EAAE,iBAAiB,CAAA;QAClC,gBAAgB,EAAE,kBAAkB,CAAA;QACpC,WAAW,EAAE,cAAc,CAAA;QAC3B,gBAAgB,EAAE,mBAAmB,CAAA;QACrC,sBAAsB,EAAE,wBAAwB,CAAA;KACjD;CACF;AAED,eAAO,MAAM,MAAM,UAQlB,CAAA;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAsO9C"}
@@ -1 +1 @@
1
- {"version":3,"file":"hub.d.ts","sourceRoot":"","sources":["../../../../src/client/locales-ru/hub.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAE9C,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CA+MhD,CAAA"}
1
+ {"version":3,"file":"hub.d.ts","sourceRoot":"","sources":["../../../../src/client/locales-ru/hub.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAE9C,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CA4NhD,CAAA"}