@khorsheed/dsh-context-guard 0.1.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 +10 -0
- package/LICENSE +21 -0
- package/README.en.md +89 -0
- package/README.i18n.yaml +6 -0
- package/README.md +89 -0
- package/cordis.patch.yml +10 -0
- package/lib/client.js +461 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +50 -0
- package/lib/invariant.js +26 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/lib/types/client/CompactGuardButton.d.ts +13 -0
- package/lib/types/client/CompactGuardButton.d.ts.map +1 -0
- package/lib/types/client/CompactGuardButton.js +53 -0
- package/lib/types/client/SettingsCard.d.ts +12 -0
- package/lib/types/client/SettingsCard.d.ts.map +1 -0
- package/lib/types/client/SettingsCard.js +97 -0
- package/lib/types/client/config.d.ts +42 -0
- package/lib/types/client/config.d.ts.map +1 -0
- package/lib/types/client/config.js +30 -0
- package/lib/types/client/guard.d.ts +44 -0
- package/lib/types/client/guard.d.ts.map +1 -0
- package/lib/types/client/guard.js +42 -0
- package/lib/types/client/index.d.ts +46 -0
- package/lib/types/client/index.d.ts.map +1 -0
- package/lib/types/client/index.js +61 -0
- package/lib/types/client/locales.d.ts +48 -0
- package/lib/types/client/locales.d.ts.map +1 -0
- package/lib/types/client/locales.js +39 -0
- package/lib/types/client/slots.d.ts +39 -0
- package/lib/types/client/slots.d.ts.map +1 -0
- package/lib/types/client/slots.js +1 -0
- package/lib/types/index.d.ts +19 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +24 -0
- package/lib/types/invariant.d.ts +16 -0
- package/lib/types/invariant.d.ts.map +1 -0
- package/lib/types/invariant.js +24 -0
- package/lib/types/namespace.d.ts +9 -0
- package/lib/types/namespace.d.ts.map +1 -0
- package/lib/types/namespace.js +8 -0
- package/lib/types/settings.d.ts +20 -0
- package/lib/types/settings.d.ts.map +1 -0
- package/lib/types/settings.js +17 -0
- package/package.json +89 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { IconWarningOutline16 } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
|
+
import { guardReading } from './guard.js';
|
|
5
|
+
import css from './CompactGuardButton.module.css';
|
|
6
|
+
/**
|
|
7
|
+
* Composer-tool-row compact button. Renders nothing until the context
|
|
8
|
+
* occupancy (`projectedTokens / contextWindow` — the same number the
|
|
9
|
+
* composer's context ring shows) crosses the threshold; then it appears
|
|
10
|
+
* automatically in the amber warning tint. The threshold comes from the
|
|
11
|
+
* shared `context-guard` settings section (live), falling back to the
|
|
12
|
+
* composition-time value while the settings surface is absent. Clicking
|
|
13
|
+
* executes the official `/compact` command through the injected face.
|
|
14
|
+
*/
|
|
15
|
+
export function CompactGuardButton({ useProjection, useConfig, t, thresholdRatio, compactNow, }) {
|
|
16
|
+
const pressure = useProjection('contextPressure');
|
|
17
|
+
const config = useConfig(value => value);
|
|
18
|
+
const effective = config.status === 'ready' && config.value !== undefined
|
|
19
|
+
? config.value
|
|
20
|
+
: { thresholdRatio };
|
|
21
|
+
const reading = useMemo(() => guardReading({
|
|
22
|
+
projectedTokens: pressure?.projectedTokens ?? pressure?.pressureTokens ?? Number.NaN,
|
|
23
|
+
contextWindow: pressure?.contextWindow,
|
|
24
|
+
thresholdRatio: effective.thresholdRatio,
|
|
25
|
+
}), [pressure, effective.thresholdRatio]);
|
|
26
|
+
const [busy, setBusy] = useState(false);
|
|
27
|
+
const [error, setError] = useState(null);
|
|
28
|
+
const aliveRef = useRef(true);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
aliveRef.current = true;
|
|
31
|
+
return () => {
|
|
32
|
+
aliveRef.current = false;
|
|
33
|
+
};
|
|
34
|
+
}, []);
|
|
35
|
+
if (reading === null || reading.level === 'ok')
|
|
36
|
+
return null;
|
|
37
|
+
const run = () => {
|
|
38
|
+
setBusy(true);
|
|
39
|
+
setError(null);
|
|
40
|
+
void compactNow().then((failure) => {
|
|
41
|
+
if (!aliveRef.current)
|
|
42
|
+
return;
|
|
43
|
+
setBusy(false);
|
|
44
|
+
setError(failure);
|
|
45
|
+
}, (reason) => {
|
|
46
|
+
if (!aliveRef.current)
|
|
47
|
+
return;
|
|
48
|
+
setBusy(false);
|
|
49
|
+
setError(reason instanceof Error ? reason.message : String(reason));
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
return (_jsxs("span", { className: css.wrap, children: [_jsxs("button", { type: "button", className: `${css.button} ${css.warning}`, "aria-label": t('button.aria', { percent: String(reading.percent) }), disabled: busy, onClick: run, children: [_jsx("span", { className: css.icon, "aria-hidden": true, children: _jsx(IconWarningOutline16, { size: 14 }) }), t('button.label')] }), error !== null && (_jsx("span", { className: css.error, role: "status", title: error, children: t('button.error') }))] }));
|
|
53
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ContextGuardSettingsCardProps } from './slots.ts';
|
|
2
|
+
/**
|
|
3
|
+
* The context-guard settings card in the plugin configuration tab: the same
|
|
4
|
+
* collapsible chrome the official plugin cards use (a header naming the
|
|
5
|
+
* plugin and what its settings govern, disclosing the controls in place),
|
|
6
|
+
* re-implemented here because the client bundle-purity gate forbids
|
|
7
|
+
* value-importing the official card chrome. Two staged number fields write
|
|
8
|
+
* through the shared settingsScope on save; per-field reset reverts a field
|
|
9
|
+
* to the composition layer.
|
|
10
|
+
*/
|
|
11
|
+
export declare function ContextGuardSettingsCard({ useConfig, scope, t }: ContextGuardSettingsCardProps): import("react").JSX.Element | null;
|
|
12
|
+
//# sourceMappingURL=SettingsCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/SettingsCard.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,YAAY,CAAA;AAuB/D;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,6BAA6B,sCAqI9F"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { IconChevronDownOutline14 } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
|
+
import css from './SettingsCard.module.css';
|
|
5
|
+
/** Parse a draft into a number, or NaN when it is not one. */
|
|
6
|
+
function parseNumber(text) {
|
|
7
|
+
const value = Number(text.trim());
|
|
8
|
+
return Number.isFinite(value) ? value : Number.NaN;
|
|
9
|
+
}
|
|
10
|
+
/** Whether the user layer carries a field (presence marks an override). */
|
|
11
|
+
function overridden(snapshot, field) {
|
|
12
|
+
return snapshot.user !== undefined
|
|
13
|
+
&& typeof snapshot.user === 'object'
|
|
14
|
+
&& snapshot.user !== null
|
|
15
|
+
&& field in snapshot.user;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The context-guard settings card in the plugin configuration tab: the same
|
|
19
|
+
* collapsible chrome the official plugin cards use (a header naming the
|
|
20
|
+
* plugin and what its settings govern, disclosing the controls in place),
|
|
21
|
+
* re-implemented here because the client bundle-purity gate forbids
|
|
22
|
+
* value-importing the official card chrome. Two staged number fields write
|
|
23
|
+
* through the shared settingsScope on save; per-field reset reverts a field
|
|
24
|
+
* to the composition layer.
|
|
25
|
+
*/
|
|
26
|
+
export function ContextGuardSettingsCard({ useConfig, scope, t }) {
|
|
27
|
+
const snapshot = useConfig(value => value);
|
|
28
|
+
const value = snapshot.status === 'ready' ? snapshot.value : undefined;
|
|
29
|
+
const [open, setOpen] = useState(false);
|
|
30
|
+
const [threshold, setThreshold] = useState({ text: '', invalid: false });
|
|
31
|
+
const [saved, setSaved] = useState(false);
|
|
32
|
+
const [error, setError] = useState(false);
|
|
33
|
+
const aliveRef = useRef(true);
|
|
34
|
+
/** The section revision the drafts were last seeded from; re-seed only on a committed change. */
|
|
35
|
+
const seededRevision = useRef(null);
|
|
36
|
+
// Re-seed the drafts whenever the section VALUE changes (first load, a save
|
|
37
|
+
// settling, an external reset). Keyed on the snapshot revision rather than
|
|
38
|
+
// the value's object identity: a save settling publishes a new snapshot
|
|
39
|
+
// (revision bump), while an unrelated re-render must not discard a draft in
|
|
40
|
+
// flight — the staged model promises the screen shows exactly what a save
|
|
41
|
+
// would store.
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
aliveRef.current = true;
|
|
44
|
+
if (value === undefined)
|
|
45
|
+
return;
|
|
46
|
+
if (seededRevision.current === snapshot.revision)
|
|
47
|
+
return;
|
|
48
|
+
seededRevision.current = snapshot.revision ?? null;
|
|
49
|
+
setThreshold({ text: String(value.thresholdRatio), invalid: false });
|
|
50
|
+
setSaved(false);
|
|
51
|
+
setError(false);
|
|
52
|
+
return () => {
|
|
53
|
+
aliveRef.current = false;
|
|
54
|
+
};
|
|
55
|
+
}, [value, snapshot.revision]);
|
|
56
|
+
if (snapshot.status === 'unavailable')
|
|
57
|
+
return null;
|
|
58
|
+
const write = (draft, min, max) => {
|
|
59
|
+
const parsed = parseNumber(draft.text);
|
|
60
|
+
setThreshold({ text: draft.text, invalid: !Number.isFinite(parsed) || parsed < min || parsed > max });
|
|
61
|
+
};
|
|
62
|
+
const save = () => {
|
|
63
|
+
if (threshold.invalid || snapshot.writable !== true)
|
|
64
|
+
return;
|
|
65
|
+
const ratio = parseNumber(threshold.text);
|
|
66
|
+
if (!Number.isFinite(ratio))
|
|
67
|
+
return;
|
|
68
|
+
setError(false);
|
|
69
|
+
setSaved(false);
|
|
70
|
+
void scope.set('thresholdRatio', ratio).then(() => {
|
|
71
|
+
if (!aliveRef.current)
|
|
72
|
+
return;
|
|
73
|
+
setSaved(true);
|
|
74
|
+
}, () => {
|
|
75
|
+
if (!aliveRef.current)
|
|
76
|
+
return;
|
|
77
|
+
setError(true);
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
const reset = () => {
|
|
81
|
+
setError(false);
|
|
82
|
+
setSaved(false);
|
|
83
|
+
void scope.unset('thresholdRatio').catch(() => {
|
|
84
|
+
if (aliveRef.current)
|
|
85
|
+
setError(true);
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
const title = t('settings.title');
|
|
89
|
+
const dirty = threshold.text !== String(value?.thresholdRatio ?? '');
|
|
90
|
+
return (_jsxs("li", { className: open ? `${css.card} ${css.cardOpen}` : css.card, children: [_jsxs("button", { type: "button", className: css.header, "aria-expanded": open, "aria-label": `${t(open ? 'settings.collapse' : 'settings.expand')}: ${title}`, onClick: () => { setOpen(!open); }, children: [_jsxs("span", { className: css.headText, children: [_jsx("span", { className: css.name, children: title }), _jsx("span", { className: css.description, children: t('settings.description') })] }), _jsx(IconChevronDownOutline14, { className: open ? `${css.chevron} ${css.chevronOpen}` : css.chevron })] }), open && value !== undefined && (_jsxs("div", { className: css.body, children: [_jsxs("label", { className: css.field, children: [_jsxs("span", { className: css.fieldHead, children: [_jsx("span", { className: css.fieldName, children: t('settings.field.threshold') }), overridden(snapshot, 'thresholdRatio') && _jsx("span", { className: css.badge, children: t('settings.overridden') })] }), _jsxs("span", { className: css.inputRow, children: [_jsx("input", { className: threshold.invalid ? `${css.input} ${css.inputInvalid}` : css.input, type: "number", step: "0.01", min: "0.01", max: "1", value: threshold.text, "aria-label": t('settings.field.threshold'), onChange: (event) => { write({ text: event.target.value, invalid: false }, 0.01, 1); } }), _jsx("button", { type: "button", className: css.reset, onClick: reset, children: t('settings.reset') })] }), _jsx("span", { className: css.hint, children: t('settings.field.threshold.hint') })] }), _jsxs("span", { className: css.footer, children: [_jsxs("span", { className: css.status, children: [saved ? _jsx("span", { className: css.saved, children: t('settings.saved') }) : null, error ? _jsx("span", { className: css.errorText, children: t('settings.error') }) : null] }), _jsx("button", { type: "button", className: css.secondary, disabled: !dirty, onClick: () => {
|
|
91
|
+
if (value === undefined)
|
|
92
|
+
return;
|
|
93
|
+
setThreshold({ text: String(value.thresholdRatio), invalid: false });
|
|
94
|
+
setSaved(false);
|
|
95
|
+
setError(false);
|
|
96
|
+
}, children: t('settings.discard') }), _jsx("button", { type: "button", className: css.save, disabled: !dirty || threshold.invalid || snapshot.writable !== true, onClick: save, children: t('settings.save') })] })] }))] }));
|
|
97
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-half configuration of the context-guard plugin. The runner hands
|
|
3
|
+
* the plugin its validated entry config through the apply second parameter;
|
|
4
|
+
* this module supplies the defaults and clamps, so a composition that does
|
|
5
|
+
* not pass config still renders with the documented values and out-of-range
|
|
6
|
+
* input lands inside the legal bounds.
|
|
7
|
+
*
|
|
8
|
+
* Once the settings surface is composed, these values arrive through the
|
|
9
|
+
* `context-guard` settings section instead: the composition entry becomes the
|
|
10
|
+
* section's base layer, and the settings card overrides it per field. The
|
|
11
|
+
* fallback path here only serves deployments without the settings surface.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Deployment-tunable guard behavior.
|
|
15
|
+
*
|
|
16
|
+
* The guard is a reminder: when the context OCCUPANCY (the same
|
|
17
|
+
* `projectedTokens / contextWindow` number the composer's context ring shows)
|
|
18
|
+
* crosses `thresholdRatio`, the compact button appears. The official
|
|
19
|
+
* compaction engine auto-compacts at 80% of the window on its own; the real
|
|
20
|
+
* provider rejection wall is `window − maxTokens` (the request reserves
|
|
21
|
+
* output), so a request can fail before occupancy alone reaches 80% — set a
|
|
22
|
+
* lower threshold for an earlier reminder. The field tunes ONLY when the
|
|
23
|
+
* button appears; real compaction timing (compaction-basic's own config) is
|
|
24
|
+
* never affected.
|
|
25
|
+
*/
|
|
26
|
+
export interface ContextGuardConfig {
|
|
27
|
+
/**
|
|
28
|
+
* Context occupancy fraction at which the button appears (clamped to
|
|
29
|
+
* (0, 1]; default 0.8, matching compaction-basic's default pressure
|
|
30
|
+
* threshold).
|
|
31
|
+
*/
|
|
32
|
+
thresholdRatio: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Normalize the entry config into the full {@link ContextGuardConfig}: every
|
|
36
|
+
* omitted field takes its documented default and every numeric field is
|
|
37
|
+
* clamped into its legal range.
|
|
38
|
+
* @param config - the unvalidated entry config, when the runner passes one.
|
|
39
|
+
* @returns the effective guard behavior.
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveConfig(config: Partial<ContextGuardConfig> | undefined): ContextGuardConfig;
|
|
42
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/client/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,cAAc,EAAE,MAAM,CAAA;CACvB;AAUD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,SAAS,GAAG,kBAAkB,CAQjG"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-half configuration of the context-guard plugin. The runner hands
|
|
3
|
+
* the plugin its validated entry config through the apply second parameter;
|
|
4
|
+
* this module supplies the defaults and clamps, so a composition that does
|
|
5
|
+
* not pass config still renders with the documented values and out-of-range
|
|
6
|
+
* input lands inside the legal bounds.
|
|
7
|
+
*
|
|
8
|
+
* Once the settings surface is composed, these values arrive through the
|
|
9
|
+
* `context-guard` settings section instead: the composition entry becomes the
|
|
10
|
+
* section's base layer, and the settings card overrides it per field. The
|
|
11
|
+
* fallback path here only serves deployments without the settings surface.
|
|
12
|
+
*/
|
|
13
|
+
/** Bounds of the deployment-tunable number (clamped in resolveConfig). */
|
|
14
|
+
const THRESHOLD_RATIO_MIN = 0.01;
|
|
15
|
+
const THRESHOLD_RATIO_MAX = 1;
|
|
16
|
+
function clamp(value, min, max) {
|
|
17
|
+
return Math.min(max, Math.max(min, value));
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Normalize the entry config into the full {@link ContextGuardConfig}: every
|
|
21
|
+
* omitted field takes its documented default and every numeric field is
|
|
22
|
+
* clamped into its legal range.
|
|
23
|
+
* @param config - the unvalidated entry config, when the runner passes one.
|
|
24
|
+
* @returns the effective guard behavior.
|
|
25
|
+
*/
|
|
26
|
+
export function resolveConfig(config) {
|
|
27
|
+
return {
|
|
28
|
+
thresholdRatio: clamp(config?.thresholdRatio ?? 0.8, THRESHOLD_RATIO_MIN, THRESHOLD_RATIO_MAX),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure guard decision math: whether the next request's prompt-plus-output
|
|
3
|
+
* budget is about to overflow the model's context window, mirroring the
|
|
4
|
+
* provider-side rejection rule (`prompt + max_tokens > context_length`).
|
|
5
|
+
*
|
|
6
|
+
* The numerator rides the official `contextPressure` projection so it answers
|
|
7
|
+
* for the NEXT request rather than the last one: `projectedTokens` is the
|
|
8
|
+
* provider-reported prompt sample carried forward over the surface's signed
|
|
9
|
+
* movement since (compaction included), with the bare sample as the fallback
|
|
10
|
+
* for logs whose projection predates that field. The occupancy is the SAME
|
|
11
|
+
* number the composer's context ring shows — the guard is a reminder on top
|
|
12
|
+
* of that ring, not a second figure.
|
|
13
|
+
*/
|
|
14
|
+
/** The three guard states: nothing to show, about to overflow, already over. */
|
|
15
|
+
export type GuardLevel = 'ok' | 'warning' | 'overdue';
|
|
16
|
+
/** Inputs to the guard decision; both come from the session's contextPressure projection plus config. */
|
|
17
|
+
export interface GuardInput {
|
|
18
|
+
/** Current projected context tokens (projectedTokens ?? pressureTokens). */
|
|
19
|
+
projectedTokens: number;
|
|
20
|
+
/** The routed model's advertised context window; absent until a route reports one. */
|
|
21
|
+
contextWindow: number | undefined;
|
|
22
|
+
/** The occupancy fraction at which the guard turns on (clamped (0, 1] by config). */
|
|
23
|
+
thresholdRatio: number;
|
|
24
|
+
}
|
|
25
|
+
/** One guard reading: the level plus the occupancy figure behind it. */
|
|
26
|
+
export interface GuardReading {
|
|
27
|
+
level: GuardLevel;
|
|
28
|
+
/** `projectedTokens / contextWindow`, rounded, clamped to 0–100 — the same occupancy the context ring shows. */
|
|
29
|
+
percent: number;
|
|
30
|
+
/** The routed model's advertised context window. */
|
|
31
|
+
contextWindow: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Decide the guard level for one request budget.
|
|
35
|
+
*
|
|
36
|
+
* `overdue` means the occupancy already reached the whole window — the
|
|
37
|
+
* provider will have rejected requests before this point, so it is a
|
|
38
|
+
* defensive floor rather than a reachable state.
|
|
39
|
+
*
|
|
40
|
+
* @param input - the projection figures plus config.
|
|
41
|
+
* @returns the reading, or null when either the window or the context is unknown.
|
|
42
|
+
*/
|
|
43
|
+
export declare function guardReading(input: GuardInput): GuardReading | null;
|
|
44
|
+
//# sourceMappingURL=guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../../src/client/guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,gFAAgF;AAChF,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,SAAS,GAAG,SAAS,CAAA;AAErD,yGAAyG;AACzG,MAAM,WAAW,UAAU;IACzB,4EAA4E;IAC5E,eAAe,EAAE,MAAM,CAAA;IACvB,sFAAsF;IACtF,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,qFAAqF;IACrF,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,wEAAwE;AACxE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,UAAU,CAAA;IACjB,gHAAgH;IAChH,OAAO,EAAE,MAAM,CAAA;IACf,oDAAoD;IACpD,aAAa,EAAE,MAAM,CAAA;CACtB;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,YAAY,GAAG,IAAI,CAiBnE"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure guard decision math: whether the next request's prompt-plus-output
|
|
3
|
+
* budget is about to overflow the model's context window, mirroring the
|
|
4
|
+
* provider-side rejection rule (`prompt + max_tokens > context_length`).
|
|
5
|
+
*
|
|
6
|
+
* The numerator rides the official `contextPressure` projection so it answers
|
|
7
|
+
* for the NEXT request rather than the last one: `projectedTokens` is the
|
|
8
|
+
* provider-reported prompt sample carried forward over the surface's signed
|
|
9
|
+
* movement since (compaction included), with the bare sample as the fallback
|
|
10
|
+
* for logs whose projection predates that field. The occupancy is the SAME
|
|
11
|
+
* number the composer's context ring shows — the guard is a reminder on top
|
|
12
|
+
* of that ring, not a second figure.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Decide the guard level for one request budget.
|
|
16
|
+
*
|
|
17
|
+
* `overdue` means the occupancy already reached the whole window — the
|
|
18
|
+
* provider will have rejected requests before this point, so it is a
|
|
19
|
+
* defensive floor rather than a reachable state.
|
|
20
|
+
*
|
|
21
|
+
* @param input - the projection figures plus config.
|
|
22
|
+
* @returns the reading, or null when either the window or the context is unknown.
|
|
23
|
+
*/
|
|
24
|
+
export function guardReading(input) {
|
|
25
|
+
const { contextWindow, projectedTokens, thresholdRatio } = input;
|
|
26
|
+
if (contextWindow === undefined || !Number.isFinite(contextWindow) || contextWindow <= 0) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
if (!Number.isFinite(projectedTokens) || projectedTokens < 0)
|
|
30
|
+
return null;
|
|
31
|
+
const ratio = projectedTokens / contextWindow;
|
|
32
|
+
const level = ratio >= 1
|
|
33
|
+
? 'overdue'
|
|
34
|
+
: ratio >= thresholdRatio
|
|
35
|
+
? 'warning'
|
|
36
|
+
: 'ok';
|
|
37
|
+
return {
|
|
38
|
+
level,
|
|
39
|
+
percent: Math.min(100, Math.round(ratio * 100)),
|
|
40
|
+
contextWindow,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context-guard plugin, browser half: contributes one
|
|
3
|
+
* `conversation.input.right` entry — a compact button inside the composer's
|
|
4
|
+
* tool row that appears automatically when the next request's budget
|
|
5
|
+
* (`contextPressure.projectedTokens` + the output budget) crosses
|
|
6
|
+
* `thresholdRatio` of the routed model's context window — plus one
|
|
7
|
+
* `settings.plugin.item` card in the plugin configuration tab that edits the
|
|
8
|
+
* same two numbers live. The action rides the official `/compact` command
|
|
9
|
+
* channel (`remote.commands.execute` → host `ctx.commands` →
|
|
10
|
+
* `ctx.compaction.compactNow`); the config rides the official settings
|
|
11
|
+
* surface (host half registers the namespace, this half binds its
|
|
12
|
+
* `settingsScope`), so neither needs a new RPC or any edit to core packages;
|
|
13
|
+
* composing this plugin out of cordis.yml removes every surface it adds.
|
|
14
|
+
*
|
|
15
|
+
* Why this exists: the official auto-compaction fires at `agent/pre-step`
|
|
16
|
+
* when the meter's context estimate crosses 80% of the window — a check that
|
|
17
|
+
* excludes the request's own output budget and rides a heuristic that
|
|
18
|
+
* deliberately underprices CJK/JSON. A long request can therefore push
|
|
19
|
+
* context + maxTokens past the window while the estimate still sits below
|
|
20
|
+
* 80%, so the provider rejects the request (CONTEXT_WINDOW_EXCEEDED). The
|
|
21
|
+
* guard surfaces the danger while a manual compaction still fits — the
|
|
22
|
+
* summarization call reserves only its own small output cap, so it keeps
|
|
23
|
+
* running after the main request is rejected.
|
|
24
|
+
* @module @khorsheed/dsh-context-guard/client
|
|
25
|
+
*/
|
|
26
|
+
import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
|
|
27
|
+
import { type ContextGuardConfig } from './config.ts';
|
|
28
|
+
import { NS } from './locales.ts';
|
|
29
|
+
export type { ContextGuardConfig } from './config.ts';
|
|
30
|
+
export { resolveConfig } from './config.ts';
|
|
31
|
+
export type { ContextGuardKey } from './locales.ts';
|
|
32
|
+
export { guardReading } from './guard.ts';
|
|
33
|
+
export type { GuardInput, GuardLevel, GuardReading } from './guard.ts';
|
|
34
|
+
export type { CompactGuardButtonProps, ContextGuardInjected, ContextGuardSettingsCardInjected, ContextGuardSettingsCardProps, } from './slots.ts';
|
|
35
|
+
/** Dictionary namespace owned by this plugin. */
|
|
36
|
+
export { NS };
|
|
37
|
+
/** Required services: the slot ledger, the command Remote, the settings scope, and the copy. */
|
|
38
|
+
export declare const inject: string[];
|
|
39
|
+
/**
|
|
40
|
+
* Client plugin body: register the composer-tool-row compact button and the
|
|
41
|
+
* settings card over the shared `context-guard` section.
|
|
42
|
+
* @param ctx - client root context.
|
|
43
|
+
* @param config - entry config (the section's composition base layer); defaults apply when the runner passes none.
|
|
44
|
+
*/
|
|
45
|
+
export declare function apply(ctx: ClientContext, config?: Partial<ContextGuardConfig>): void;
|
|
46
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,EAAE,aAAa,EAAa,MAAM,wCAAwC,CAAA;AAiBtF,OAAO,EAAiB,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAA;AACpE,OAAO,EAAM,EAAE,EAAM,MAAM,cAAc,CAAA;AAKzC,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACzC,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACtE,YAAY,EACV,uBAAuB,EAAE,oBAAoB,EAAE,gCAAgC,EAAE,6BAA6B,GAC/G,MAAM,YAAY,CAAA;AAEnB,iDAAiD;AACjD,OAAO,EAAE,EAAE,EAAE,CAAA;AAEb,gGAAgG;AAChG,eAAO,MAAM,MAAM,UAAoE,CAAA;AAEvF;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,IAAI,CA4CpF"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { CONTEXT_GUARD_NS } from '../namespace.js';
|
|
2
|
+
import { resolveConfig } from './config.js';
|
|
3
|
+
import { en, NS, zh } from './locales.js';
|
|
4
|
+
import { CompactGuardButton } from './CompactGuardButton.js';
|
|
5
|
+
import { ContextGuardSettingsCard } from './SettingsCard.js';
|
|
6
|
+
export { resolveConfig } from './config.js';
|
|
7
|
+
export { guardReading } from './guard.js';
|
|
8
|
+
/** Dictionary namespace owned by this plugin. */
|
|
9
|
+
export { NS };
|
|
10
|
+
/** Required services: the slot ledger, the command Remote, the settings scope, and the copy. */
|
|
11
|
+
export const inject = ['slots', 'remote', 'remote.commands', 'locale', 'settingsScope'];
|
|
12
|
+
/**
|
|
13
|
+
* Client plugin body: register the composer-tool-row compact button and the
|
|
14
|
+
* settings card over the shared `context-guard` section.
|
|
15
|
+
* @param ctx - client root context.
|
|
16
|
+
* @param config - entry config (the section's composition base layer); defaults apply when the runner passes none.
|
|
17
|
+
*/
|
|
18
|
+
export function apply(ctx, config) {
|
|
19
|
+
const fallback = resolveConfig(config);
|
|
20
|
+
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'context-guard: dictionaries');
|
|
21
|
+
// One shared live section: the settings card writes it, the button reads
|
|
22
|
+
// it. While the settings surface is absent, the button falls back to the
|
|
23
|
+
// composition-time values above.
|
|
24
|
+
const scope = ctx.settingsScope.bind({ namespace: CONTEXT_GUARD_NS });
|
|
25
|
+
// The slot is declared by ui-conversation, whose apply order relative to
|
|
26
|
+
// this plugin is unconstrained: register through slots.inject so the entry
|
|
27
|
+
// waits for the declaration instead of crashing the loader at boot.
|
|
28
|
+
ctx.slots.inject('conversation.input.right', () => ctx.slots.register({
|
|
29
|
+
name: 'conversation.input.right',
|
|
30
|
+
id: 'context-guard',
|
|
31
|
+
// Sit before the primary send button; the seat currently has no other
|
|
32
|
+
// occupants, and a leading position keeps the danger visible.
|
|
33
|
+
order: -10,
|
|
34
|
+
locale: NS,
|
|
35
|
+
inject: (sessionId) => ({
|
|
36
|
+
thresholdRatio: fallback.thresholdRatio,
|
|
37
|
+
// Failure strings stay English (error-surface policy: not localized).
|
|
38
|
+
compactNow: async () => {
|
|
39
|
+
const result = await ctx.remote.commands.execute(sessionId, '/compact', []);
|
|
40
|
+
if (!result.ok)
|
|
41
|
+
return `${result.error.message} (${result.error.code})`;
|
|
42
|
+
if (result.value === undefined)
|
|
43
|
+
return 'unknown command: /compact';
|
|
44
|
+
return null;
|
|
45
|
+
},
|
|
46
|
+
hooks: { config: scope },
|
|
47
|
+
}),
|
|
48
|
+
}, CompactGuardButton));
|
|
49
|
+
// The plugin configuration tab keys its cards on the settings namespace, so
|
|
50
|
+
// the compact-timing card registers under CONTEXT_GUARD_NS and renders
|
|
51
|
+
// wherever the tab dispatches that key.
|
|
52
|
+
ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({
|
|
53
|
+
name: 'settings.plugin.item',
|
|
54
|
+
key: CONTEXT_GUARD_NS,
|
|
55
|
+
locale: NS,
|
|
56
|
+
inject: () => ({
|
|
57
|
+
scope,
|
|
58
|
+
hooks: { config: scope },
|
|
59
|
+
}),
|
|
60
|
+
}, ContextGuardSettingsCard));
|
|
61
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** `context-guard` namespace dictionaries. */
|
|
2
|
+
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
3
|
+
export declare const zh: {
|
|
4
|
+
'button.label': string;
|
|
5
|
+
'button.aria': string;
|
|
6
|
+
'button.error': string;
|
|
7
|
+
'settings.title': string;
|
|
8
|
+
'settings.description': string;
|
|
9
|
+
'settings.expand': string;
|
|
10
|
+
'settings.collapse': string;
|
|
11
|
+
'settings.field.threshold': string;
|
|
12
|
+
'settings.field.threshold.hint': string;
|
|
13
|
+
'settings.overridden': string;
|
|
14
|
+
'settings.reset': string;
|
|
15
|
+
'settings.save': string;
|
|
16
|
+
'settings.discard': string;
|
|
17
|
+
'settings.saved': string;
|
|
18
|
+
'settings.error': string;
|
|
19
|
+
};
|
|
20
|
+
/** The context-guard namespace key union. */
|
|
21
|
+
export type ContextGuardKey = keyof typeof zh;
|
|
22
|
+
/** The dictionary namespace owned by this plugin. */
|
|
23
|
+
export declare const NS = "context-guard";
|
|
24
|
+
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
25
|
+
interface LocaleNamespaceMap {
|
|
26
|
+
/** The context-guard compact button's copy. */
|
|
27
|
+
'context-guard': ContextGuardKey;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** English dictionary, checked complete against the zh key set. */
|
|
31
|
+
export declare const en: {
|
|
32
|
+
'button.label': string;
|
|
33
|
+
'button.aria': string;
|
|
34
|
+
'button.error': string;
|
|
35
|
+
'settings.title': string;
|
|
36
|
+
'settings.description': string;
|
|
37
|
+
'settings.expand': string;
|
|
38
|
+
'settings.collapse': string;
|
|
39
|
+
'settings.field.threshold': string;
|
|
40
|
+
'settings.field.threshold.hint': string;
|
|
41
|
+
'settings.overridden': string;
|
|
42
|
+
'settings.reset': string;
|
|
43
|
+
'settings.save': string;
|
|
44
|
+
'settings.discard': string;
|
|
45
|
+
'settings.saved': string;
|
|
46
|
+
'settings.error': string;
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=locales.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAE9C,mEAAmE;AACnE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;CAgBmB,CAAA;AAElC,6CAA6C;AAC7C,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,EAAE,CAAA;AAE7C,qDAAqD;AACrD,eAAO,MAAM,EAAE,kBAAkB,CAAA;AAEjC,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,+CAA+C;QAC/C,eAAe,EAAE,eAAe,CAAA;KACjC;CACF;AAED,mEAAmE;AACnE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;CAgBmB,CAAA"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** `context-guard` namespace dictionaries. */
|
|
2
|
+
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
3
|
+
export const zh = {
|
|
4
|
+
'button.label': '压缩',
|
|
5
|
+
'button.aria': '上下文已用 {percent}%,点击压缩历史',
|
|
6
|
+
'button.error': '压缩未执行',
|
|
7
|
+
'settings.title': '压缩提醒时机',
|
|
8
|
+
'settings.description': '上下文占用达到设定比例时,输入框出现压缩按钮',
|
|
9
|
+
'settings.expand': '展开',
|
|
10
|
+
'settings.collapse': '收起',
|
|
11
|
+
'settings.field.threshold': '提醒比例',
|
|
12
|
+
'settings.field.threshold.hint': '上下文占用达到窗口的该比例时显示压缩按钮;可配置范围 0.01–1,按自己的偏好调整即可',
|
|
13
|
+
'settings.overridden': '已自定义',
|
|
14
|
+
'settings.reset': '重置',
|
|
15
|
+
'settings.save': '保存',
|
|
16
|
+
'settings.discard': '放弃',
|
|
17
|
+
'settings.saved': '已保存',
|
|
18
|
+
'settings.error': '保存失败,请重试',
|
|
19
|
+
};
|
|
20
|
+
/** The dictionary namespace owned by this plugin. */
|
|
21
|
+
export const NS = 'context-guard';
|
|
22
|
+
/** English dictionary, checked complete against the zh key set. */
|
|
23
|
+
export const en = {
|
|
24
|
+
'button.label': 'compact',
|
|
25
|
+
'button.aria': 'Context at {percent}%, click to compact history',
|
|
26
|
+
'button.error': 'Compaction was not executed',
|
|
27
|
+
'settings.title': 'Compaction reminder timing',
|
|
28
|
+
'settings.description': 'Shows the compact button in the composer once context occupancy reaches the configured fraction',
|
|
29
|
+
'settings.expand': 'Expand',
|
|
30
|
+
'settings.collapse': 'Collapse',
|
|
31
|
+
'settings.field.threshold': 'Reminder ratio',
|
|
32
|
+
'settings.field.threshold.hint': 'Show the compact button once context occupancy reaches this fraction of the window; configurable from 0.01 to 1 — tune it to your preference',
|
|
33
|
+
'settings.overridden': 'Overridden',
|
|
34
|
+
'settings.reset': 'Reset',
|
|
35
|
+
'settings.save': 'Save',
|
|
36
|
+
'settings.discard': 'Discard',
|
|
37
|
+
'settings.saved': 'Saved',
|
|
38
|
+
'settings.error': 'Save failed, please retry',
|
|
39
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slot-facing types of the context-guard client half: the injected faces of
|
|
3
|
+
* the composer-tool-row compact button and the settings card, both carrying
|
|
4
|
+
* the shared `context-guard` settings scope as a hooks source so the button
|
|
5
|
+
* and the card react to the same live section.
|
|
6
|
+
*/
|
|
7
|
+
import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
8
|
+
import type { SettingsScope } from '@deepseek-ai/dsh-client-runtime/client';
|
|
9
|
+
import type { ContextGuardConfig } from './config.ts';
|
|
10
|
+
/** Injected action face of the composer-tool-row entry. */
|
|
11
|
+
export interface ContextGuardInjected {
|
|
12
|
+
/** Resolved context-occupancy fraction at which the guard turns on; the boot-time fallback. */
|
|
13
|
+
thresholdRatio: number;
|
|
14
|
+
/**
|
|
15
|
+
* Run the official `/compact` command against this session's agent —
|
|
16
|
+
* the same command the user would type, so the host owns the lifecycle
|
|
17
|
+
* (idle-gating, the compaction lock, and the flow node presentation).
|
|
18
|
+
* @returns null on admitted execution; a user-visible failure line otherwise.
|
|
19
|
+
*/
|
|
20
|
+
compactNow: () => Promise<string | null>;
|
|
21
|
+
hooks: {
|
|
22
|
+
/** The shared `context-guard` settings section; the button binds `useConfig` to it. */
|
|
23
|
+
config: SettingsScope<ContextGuardConfig>;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** Full props of the composer-tool-row compact entry. */
|
|
27
|
+
export type CompactGuardButtonProps = PropsRuntime<'conversation.input.right'> & InjectFace<ContextGuardInjected> & PropsLocale<'context-guard'>;
|
|
28
|
+
/** Injected face of the settings card: the same shared section, read and written here. */
|
|
29
|
+
export interface ContextGuardSettingsCardInjected {
|
|
30
|
+
/** The live section handle; the card stages edits and writes through it on save. */
|
|
31
|
+
scope: SettingsScope<ContextGuardConfig>;
|
|
32
|
+
hooks: {
|
|
33
|
+
/** The shared `context-guard` settings section; the card binds `useConfig` to it. */
|
|
34
|
+
config: SettingsScope<ContextGuardConfig>;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** Full props of the settings.plugin.item card entry. */
|
|
38
|
+
export type ContextGuardSettingsCardProps = PropsRuntime<'settings.plugin.item'> & InjectFace<ContextGuardSettingsCardInjected> & PropsLocale<'context-guard'>;
|
|
39
|
+
//# sourceMappingURL=slots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slots.d.ts","sourceRoot":"","sources":["../../../src/client/slots.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAC7F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAS3E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAErD,2DAA2D;AAC3D,MAAM,WAAW,oBAAoB;IACnC,+FAA+F;IAC/F,cAAc,EAAE,MAAM,CAAA;IACtB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IACxC,KAAK,EAAE;QACL,uFAAuF;QACvF,MAAM,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAA;KAC1C,CAAA;CACF;AAED,yDAAyD;AACzD,MAAM,MAAM,uBAAuB,GACjC,YAAY,CAAC,0BAA0B,CAAC,GACtC,UAAU,CAAC,oBAAoB,CAAC,GAChC,WAAW,CAAC,eAAe,CAAC,CAAA;AAEhC,0FAA0F;AAC1F,MAAM,WAAW,gCAAgC;IAC/C,oFAAoF;IACpF,KAAK,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAA;IACxC,KAAK,EAAE;QACL,qFAAqF;QACrF,MAAM,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAA;KAC1C,CAAA;CACF;AAED,yDAAyD;AACzD,MAAM,MAAM,6BAA6B,GACvC,YAAY,CAAC,sBAAsB,CAAC,GAClC,UAAU,CAAC,gCAAgC,CAAC,GAC5C,WAAW,CAAC,eAAe,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context-guard plugin, node half. Registers the `context-guard` settings
|
|
3
|
+
* namespace so the web settings page can render the compact-timing card and
|
|
4
|
+
* the browser half can bind its `settingsScope` to the same section. The
|
|
5
|
+
* plugin's own runtime behavior is purely client-side (the composer compact
|
|
6
|
+
* button); this registration only exists to make the two tunables
|
|
7
|
+
* (`thresholdRatio`, `maxTokens`) editable from the GUI instead of YAML.
|
|
8
|
+
* @module @khorsheed/dsh-context-guard
|
|
9
|
+
*/
|
|
10
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
11
|
+
export { CONTEXT_GUARD_NS } from './namespace.ts';
|
|
12
|
+
export { ContextGuardSettingsSchema } from './settings.ts';
|
|
13
|
+
/**
|
|
14
|
+
* Register the compact-timing section when a settings provider exists.
|
|
15
|
+
* @param ctx - Host context whose optional settings service owns the section.
|
|
16
|
+
* @param config - the plugin's composition entry, used as the section's base layer.
|
|
17
|
+
*/
|
|
18
|
+
export declare function apply(ctx: Context, config?: Record<string, unknown>): void;
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAKlD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAA;AAE1D;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,IAAI,CAQ9E"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context-guard plugin, node half. Registers the `context-guard` settings
|
|
3
|
+
* namespace so the web settings page can render the compact-timing card and
|
|
4
|
+
* the browser half can bind its `settingsScope` to the same section. The
|
|
5
|
+
* plugin's own runtime behavior is purely client-side (the composer compact
|
|
6
|
+
* button); this registration only exists to make the two tunables
|
|
7
|
+
* (`thresholdRatio`, `maxTokens`) editable from the GUI instead of YAML.
|
|
8
|
+
* @module @khorsheed/dsh-context-guard
|
|
9
|
+
*/
|
|
10
|
+
import { settingsNamespace } from '@deepseek-ai/dsh-settings';
|
|
11
|
+
import { CONTEXT_GUARD_NS } from './namespace.js';
|
|
12
|
+
import { ContextGuardSettingsSchema } from './settings.js';
|
|
13
|
+
export { CONTEXT_GUARD_NS } from './namespace.js';
|
|
14
|
+
export { ContextGuardSettingsSchema } from './settings.js';
|
|
15
|
+
/**
|
|
16
|
+
* Register the compact-timing section when a settings provider exists.
|
|
17
|
+
* @param ctx - Host context whose optional settings service owns the section.
|
|
18
|
+
* @param config - the plugin's composition entry, used as the section's base layer.
|
|
19
|
+
*/
|
|
20
|
+
export function apply(ctx, config = {}) {
|
|
21
|
+
ctx.inject(['settings'], (settingsCtx) => {
|
|
22
|
+
settingsCtx.settings.register(settingsNamespace(CONTEXT_GUARD_NS), ContextGuardSettingsSchema, { base: config });
|
|
23
|
+
});
|
|
24
|
+
}
|