@t4r71/dsh-dual-axis 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/LICENSE +21 -0
- package/README.md +147 -0
- package/cordis.patch.yml +68 -0
- package/lib/fs.js +419 -0
- package/lib/index.js +2591 -0
- package/lib/read-guard.js +1812 -0
- package/lib/session-store.js +978 -0
- package/lib/types/axis-command.d.ts +59 -0
- package/lib/types/axis-entry.d.ts +45 -0
- package/lib/types/axis.d.ts +131 -0
- package/lib/types/config.d.ts +173 -0
- package/lib/types/containment.d.ts +27 -0
- package/lib/types/content.d.ts +68 -0
- package/lib/types/fs-fence.d.ts +196 -0
- package/lib/types/fs.d.ts +15 -0
- package/lib/types/groups.d.ts +226 -0
- package/lib/types/index.d.ts +205 -0
- package/lib/types/read-guard.d.ts +34 -0
- package/lib/types/scope-prompt.d.ts +130 -0
- package/lib/types/scope.d.ts +108 -0
- package/lib/types/session-axes.d.ts +51 -0
- package/lib/types/session-store.d.ts +496 -0
- package/package.json +103 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The READ-AXIS FENCE: the filesystem backend that enforces the session's read
|
|
3
|
+
* axis in addition to 0.1.7's write fence.
|
|
4
|
+
*
|
|
5
|
+
* NOT MOUNTED, AND NOT WIRED TO THE SESSION AXIS STORE.
|
|
6
|
+
*
|
|
7
|
+
* Both facts are deliberate and both are load-bearing for anyone reading this
|
|
8
|
+
* file, so they are stated here rather than left to the composition:
|
|
9
|
+
*
|
|
10
|
+
* - NOT MOUNTED. No profile composes the `dual-axis-fs` row; this package's own
|
|
11
|
+
* `cordis.patch.yml` carries it commented out, because installing this backend
|
|
12
|
+
* means taking the `fs` service away from the composition's own
|
|
13
|
+
* `fs-sandbox` row (`packages/fs/fs-sandbox/src/index.ts`), and two live rows
|
|
14
|
+
* registering one service fail startup outright.
|
|
15
|
+
* - NOT WIRED. {@link ReadAxes} is a TRAILING PARAMETER on each override, the
|
|
16
|
+
* 0.1.6 way of threading a policy. Nothing produces it: the session-qualified
|
|
17
|
+
* read fence actually in force is `./read-guard.ts`, which takes its session
|
|
18
|
+
* from `tools/pre-execute` because 0.1.7's filesystem read path carries no
|
|
19
|
+
* session at all. A caller that supplies no axes gets
|
|
20
|
+
* {@link DualAxisFileSystem.defaultAxes}, i.e. the deployment policy — never
|
|
21
|
+
* this package's per-session axis store (`./session-store.ts`). Mounting this
|
|
22
|
+
* backend WITHOUT wiring it therefore enforces the deployment default read
|
|
23
|
+
* axis for every session, which is not what the two composer dropdowns say.
|
|
24
|
+
*
|
|
25
|
+
* What it is still worth keeping for: {@link readAxisRefusal} and
|
|
26
|
+
* {@link readAxisRefusalAsync} are the canonical containment predicates for a
|
|
27
|
+
* read axis (sync lexical, async identity-resolving), and `tests/axis.spec.ts`
|
|
28
|
+
* exercises every branch of them with no filesystem, session, or service.
|
|
29
|
+
* Retiring the subclass means retiring that coverage with it.
|
|
30
|
+
*
|
|
31
|
+
* 0.1.7 deleted the read fence outright. Its `@deepseek-ai/dsh-fs-sandbox`
|
|
32
|
+
* (147 lines) states the removal in its own header: "Reads pass through
|
|
33
|
+
* untouched: every mode permits reading", and it overrides only
|
|
34
|
+
* `writeText` / `editText`. 0.1.6's 358-line version fenced eight read
|
|
35
|
+
* entry points. This subclass restores that fence on 0.1.7's own interface.
|
|
36
|
+
*
|
|
37
|
+
* It extends 0.1.7's `SandboxedFileSystem` rather than `LocalFileSystem`, so
|
|
38
|
+
* the write fence is inherited unchanged and this class adds exactly one
|
|
39
|
+
* thing: the read-path check. Every read override delegates to `super`,
|
|
40
|
+
* which is the sandboxed write class's own inherited implementation — no
|
|
41
|
+
* storage mechanic is re-implemented here.
|
|
42
|
+
*
|
|
43
|
+
* The fence is a policy check in TRUSTED code over a MODEL-CONTROLLED path,
|
|
44
|
+
* NOT a kernel boundary: canonicalize-then-contain is the complete answer to
|
|
45
|
+
* this surface.
|
|
46
|
+
*
|
|
47
|
+
* @module @t4r71/dsh-dual-axis/fs-fence
|
|
48
|
+
*/
|
|
49
|
+
import { Context } from '@deepseek-ai/cordis';
|
|
50
|
+
import { SandboxedFileSystem } from '@deepseek-ai/dsh-fs-sandbox';
|
|
51
|
+
import type { FsDirEntry, FsInfo, FsPathInfo, FsTarget } from '@deepseek-ai/dsh-fs';
|
|
52
|
+
import { type AxisScope, type EffectiveScopes } from './axis.ts';
|
|
53
|
+
/** The axis pair a read carries; omitted members fall back to the deployment default. */
|
|
54
|
+
export type ReadAxes = Partial<Pick<EffectiveScopes, 'read'>>;
|
|
55
|
+
/**
|
|
56
|
+
* Decide whether one read axis permits one canonical target key. This is the
|
|
57
|
+
* fence's entire decision, as a pure function: it takes the evaluated range and
|
|
58
|
+
* the containment predicate, so a test can exercise every branch with no
|
|
59
|
+
* filesystem, no session, and no service.
|
|
60
|
+
*
|
|
61
|
+
* The error message names the READ axis's kind, never the mode that would
|
|
62
|
+
* spell it: a read axis of `workspace` would otherwise be reported as
|
|
63
|
+
* "workspace-write", a WRITE mode name this axis has no concept of.
|
|
64
|
+
* @param read - the read axis in force.
|
|
65
|
+
* @param policy - the workspace root a `workspace` read axis derives from.
|
|
66
|
+
* @param targetKey - the target's canonical identity key.
|
|
67
|
+
* @param contains - the containment predicate.
|
|
68
|
+
* @returns `undefined` when permitted, or the refusal message.
|
|
69
|
+
*/
|
|
70
|
+
export declare function readAxisRefusal(read: AxisScope, policy: {
|
|
71
|
+
workspaceRoot: string;
|
|
72
|
+
}, targetKey: string, contains: (path: string, root: string) => boolean): string | undefined;
|
|
73
|
+
/**
|
|
74
|
+
* The async twin of {@link readAxisRefusal}: production containment
|
|
75
|
+
* (`isPathUnder`) walks the filesystem identity chain for alias-equivalent
|
|
76
|
+
* roots, so the fence cannot use the lexical predicate alone.
|
|
77
|
+
* @param read - the read axis in force.
|
|
78
|
+
* @param policy - the workspace root a `workspace` read axis derives from.
|
|
79
|
+
* @param targetKey - the target's canonical identity key.
|
|
80
|
+
* @param contains - the async containment predicate.
|
|
81
|
+
* @returns `undefined` when permitted, or the refusal message.
|
|
82
|
+
*/
|
|
83
|
+
export declare function readAxisRefusalAsync(read: AxisScope, policy: {
|
|
84
|
+
workspaceRoot: string;
|
|
85
|
+
}, targetKey: string, contains: (path: string, root: string) => Promise<boolean>): Promise<string | undefined>;
|
|
86
|
+
/**
|
|
87
|
+
* The read-axis-enforcing filesystem backend. Mount it in place of
|
|
88
|
+
* `@deepseek-ai/dsh-fs-sandbox`: it registers the same `fs` service and
|
|
89
|
+
* inherits that backend's write fence verbatim.
|
|
90
|
+
*/
|
|
91
|
+
export declare class DualAxisFileSystem extends SandboxedFileSystem {
|
|
92
|
+
constructor(ctx: Context, config: ConstructorParameters<typeof SandboxedFileSystem>[1]);
|
|
93
|
+
/**
|
|
94
|
+
* The read axis pair of the session a read belongs to. Reads carry no
|
|
95
|
+
* session, so the fence resolves the DEPLOYMENT default axes here; a caller
|
|
96
|
+
* that knows the calling session passes them per call instead.
|
|
97
|
+
* @returns the deployment default axis pair.
|
|
98
|
+
*/
|
|
99
|
+
protected defaultAxes(): EffectiveScopes;
|
|
100
|
+
/**
|
|
101
|
+
* Fence the caller's read path against the READ axis, then return the target
|
|
102
|
+
* for the inherited read. The check happens BEFORE any disk access.
|
|
103
|
+
* @param target - the target the caller already resolved.
|
|
104
|
+
* @param axes - the calling session's axis pair; omit for the deployment default.
|
|
105
|
+
* @returns the same target, once the read axis permits it.
|
|
106
|
+
* @throws {FsError} `FS_SANDBOX_DENIED` when the read axis refuses the target.
|
|
107
|
+
*/
|
|
108
|
+
protected checkedRead(target: FsTarget, axes?: ReadAxes): Promise<FsTarget>;
|
|
109
|
+
/**
|
|
110
|
+
* Fence `lstat`'s string path by resolving the same target the inherited
|
|
111
|
+
* implementation will inspect, then applying the read axis. The resolution
|
|
112
|
+
* happens BEFORE the metadata read, so a refusal never touches the disk, and
|
|
113
|
+
* the inherited call still receives the original arguments verbatim.
|
|
114
|
+
* @param path - the requested path, relative to `opts.cwd` or the backend cwd.
|
|
115
|
+
* @param opts - optional cwd for the resolution.
|
|
116
|
+
* @param axes - the calling session's axis pair; omit for the deployment default.
|
|
117
|
+
* @returns the target the inherited `lstat` will inspect.
|
|
118
|
+
*/
|
|
119
|
+
protected checkedReadPath(path: string, opts?: {
|
|
120
|
+
cwd?: string;
|
|
121
|
+
}, axes?: ReadAxes): Promise<FsTarget>;
|
|
122
|
+
/**
|
|
123
|
+
* Fence the read by the per-call read axis, then delegate to the inherited
|
|
124
|
+
* UTF-8 read.
|
|
125
|
+
* @param target - the resolved target to read.
|
|
126
|
+
* @param signal - aborts the read.
|
|
127
|
+
* @param axes - the calling session's axis pair; omit for the deployment default.
|
|
128
|
+
* @returns the file content from the inherited backend.
|
|
129
|
+
*/
|
|
130
|
+
readText(target: FsTarget, signal?: AbortSignal, axes?: ReadAxes): Promise<string>;
|
|
131
|
+
/**
|
|
132
|
+
* Fence the read by the per-call read axis, then delegate to the inherited
|
|
133
|
+
* streaming read. The fence runs BEFORE the iterable is handed out: a refused
|
|
134
|
+
* stream never produces a chunk.
|
|
135
|
+
* @param target - the resolved target to stream.
|
|
136
|
+
* @param signal - aborts the read.
|
|
137
|
+
* @param axes - the calling session's axis pair; omit for the deployment default.
|
|
138
|
+
* @returns the inherited chunk iterable.
|
|
139
|
+
*/
|
|
140
|
+
streamText(target: FsTarget, signal?: AbortSignal, axes?: ReadAxes): Promise<AsyncIterable<string>>;
|
|
141
|
+
/**
|
|
142
|
+
* Fence the read by the per-call read axis, then delegate to the inherited
|
|
143
|
+
* bounded byte read.
|
|
144
|
+
* @param target - the resolved target to read.
|
|
145
|
+
* @param signal - aborts the read.
|
|
146
|
+
* @param maxBytes - inclusive byte cap on the complete content.
|
|
147
|
+
* @param axes - the calling session's axis pair; omit for the deployment default.
|
|
148
|
+
* @returns the file bytes from the inherited backend.
|
|
149
|
+
*/
|
|
150
|
+
readBytes(target: FsTarget, signal: AbortSignal | undefined, maxBytes: number, axes?: ReadAxes): Promise<Uint8Array>;
|
|
151
|
+
/**
|
|
152
|
+
* Fence the read by the per-call read axis, then delegate to the inherited
|
|
153
|
+
* byte-window read.
|
|
154
|
+
* @param target - the resolved target to read.
|
|
155
|
+
* @param range - `offset` and `length` of the window.
|
|
156
|
+
* @param signal - aborts the read.
|
|
157
|
+
* @param axes - the calling session's axis pair; omit for the deployment default.
|
|
158
|
+
* @returns the byte window from the inherited backend.
|
|
159
|
+
*/
|
|
160
|
+
readByteRange(target: FsTarget, range: {
|
|
161
|
+
offset: number;
|
|
162
|
+
length: number;
|
|
163
|
+
}, signal?: AbortSignal, axes?: ReadAxes): Promise<Uint8Array>;
|
|
164
|
+
/**
|
|
165
|
+
* Fence the listing by the per-call read axis, then delegate to the inherited
|
|
166
|
+
* directory listing.
|
|
167
|
+
* @param target - the resolved directory to list.
|
|
168
|
+
* @param signal - aborts the listing.
|
|
169
|
+
* @param axes - the calling session's axis pair; omit for the deployment default.
|
|
170
|
+
* @returns the directory entries from the inherited backend.
|
|
171
|
+
*/
|
|
172
|
+
listDir(target: FsTarget, signal?: AbortSignal, axes?: ReadAxes): Promise<FsDirEntry[]>;
|
|
173
|
+
/**
|
|
174
|
+
* Fence the metadata read by the per-call read axis, then delegate to the
|
|
175
|
+
* inherited stat.
|
|
176
|
+
* @param target - the resolved target to inspect.
|
|
177
|
+
* @param signal - aborts the metadata read.
|
|
178
|
+
* @param axes - the calling session's axis pair; omit for the deployment default.
|
|
179
|
+
* @returns the metadata, or `undefined` when the target is absent.
|
|
180
|
+
*/
|
|
181
|
+
stat(target: FsTarget, signal?: AbortSignal, axes?: ReadAxes): Promise<FsInfo | undefined>;
|
|
182
|
+
/**
|
|
183
|
+
* Fence the no-follow metadata read by the per-call read axis, then delegate
|
|
184
|
+
* to the inherited lstat with the caller's arguments unchanged.
|
|
185
|
+
* @param path - the requested path, relative to `opts.cwd` or the backend cwd.
|
|
186
|
+
* @param opts - optional cwd for the resolution.
|
|
187
|
+
* @param signal - aborts the metadata read.
|
|
188
|
+
* @param axes - the calling session's axis pair; omit for the deployment default.
|
|
189
|
+
* @returns the path-entry metadata, or `undefined` when the entry is absent.
|
|
190
|
+
*/
|
|
191
|
+
lstat(path: string, opts?: {
|
|
192
|
+
cwd?: string;
|
|
193
|
+
}, signal?: AbortSignal, axes?: ReadAxes): Promise<FsPathInfo | undefined>;
|
|
194
|
+
}
|
|
195
|
+
export default DualAxisFileSystem;
|
|
196
|
+
//# sourceMappingURL=fs-fence.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The filesystem backend half of the dual-axis bundle, as its own entry point.
|
|
3
|
+
*
|
|
4
|
+
* It lives behind the `@t4r71/dsh-dual-axis/fs` subpath so the bundle's
|
|
5
|
+
* default entry stays free of a `dsh-fs-sandbox` runtime import: a deployment
|
|
6
|
+
* that only wants the axes and the settings page mounts the `dual-axis` row,
|
|
7
|
+
* and a deployment replacing the sandboxed filesystem mounts
|
|
8
|
+
* `dual-axis-fs` (both rows are in this package's `cordis.patch.yml`).
|
|
9
|
+
*
|
|
10
|
+
* @module @t4r71/dsh-dual-axis/fs
|
|
11
|
+
*/
|
|
12
|
+
export { DualAxisFileSystem, readAxisRefusal, readAxisRefusalAsync } from './fs-fence.ts';
|
|
13
|
+
export type { ReadAxes } from './fs-fence.ts';
|
|
14
|
+
export { DualAxisFileSystem as default } from './fs-fence.ts';
|
|
15
|
+
//# sourceMappingURL=fs.d.ts.map
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RULE GROUPS and THE ONE resolution from a session's axis preset to the ranges
|
|
3
|
+
* actually in force.
|
|
4
|
+
*
|
|
5
|
+
* A rule group is a named fragment of path rules owned by the settings page
|
|
6
|
+
* (`dual-axis`'s `groups` Config field). A session references groups BY ID and
|
|
7
|
+
* never stores their content: ten sessions selecting one group must not become
|
|
8
|
+
* ten copies of it, because copies drift. Resolving an id against the CURRENT
|
|
9
|
+
* library is therefore the only place a group's rules enter a decision, and
|
|
10
|
+
* editing a group takes effect on every session referencing it without a
|
|
11
|
+
* restart.
|
|
12
|
+
*
|
|
13
|
+
* This module is that place, for both axes and for both invariants that ride on
|
|
14
|
+
* them:
|
|
15
|
+
*
|
|
16
|
+
* - **deny wins over allow**, with session-level entries and every selected
|
|
17
|
+
* group merged into ONE allow list and ONE deny list first
|
|
18
|
+
* ({@link resolveEffectiveAxes}), so a group's removal cannot be overridden by
|
|
19
|
+
* a session-level addition.
|
|
20
|
+
* - **write never exceeds read**: the effective write range is the write axis's
|
|
21
|
+
* range INTERSECTED with the read axis's range. A write outside the read range
|
|
22
|
+
* would let an agent overwrite a file it cannot read back, so the intersection
|
|
23
|
+
* is computed here — in the resolution — and never in a user interface.
|
|
24
|
+
*
|
|
25
|
+
* Both the fence and the model-facing prompt call {@link resolveEffectiveAxes},
|
|
26
|
+
* so the range the model is told about and the range it is held to are the same
|
|
27
|
+
* computation over the same inputs: the session's own preset (read and write
|
|
28
|
+
* axes, their own additions and removals, and the group ids they selected) plus
|
|
29
|
+
* the current definitions of those ids. The settings page's `read` / `write`
|
|
30
|
+
* defaults and its `defaultGroups` seed are NOT among those inputs: they are
|
|
31
|
+
* read once, when a session is created.
|
|
32
|
+
*
|
|
33
|
+
* A reference to an id the library does not define is refused, never treated as
|
|
34
|
+
* an empty group: "I excluded it and nothing happened" is the worst failure this
|
|
35
|
+
* surface can have.
|
|
36
|
+
*
|
|
37
|
+
* @module @t4r71/dsh-dual-axis/groups
|
|
38
|
+
*/
|
|
39
|
+
import type { AxisScope, EffectiveScopes } from './axis.ts';
|
|
40
|
+
import type { ResolvedScope, ScopePolicy } from './scope.ts';
|
|
41
|
+
/** One group's rules for one axis. Either list may be absent. */
|
|
42
|
+
export interface RuleGroupAxis {
|
|
43
|
+
/** Absolute roots this group adds to the base. */
|
|
44
|
+
readonly allow?: readonly string[];
|
|
45
|
+
/** Absolute roots this group removes even from the base — removal wins. */
|
|
46
|
+
readonly deny?: readonly string[];
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A named rule fragment. `id` is what a session stores; `name` is for humans
|
|
50
|
+
* only and may change freely. A group may carry rules for one axis, the other,
|
|
51
|
+
* or both.
|
|
52
|
+
*/
|
|
53
|
+
export interface RuleGroup {
|
|
54
|
+
/** Stable identity a session references; renaming it changes every reference. */
|
|
55
|
+
readonly id: string;
|
|
56
|
+
/** The human-readable name; carries no semantics. */
|
|
57
|
+
readonly name: string;
|
|
58
|
+
/** The rules this group contributes to the read axis. */
|
|
59
|
+
readonly read?: RuleGroupAxis;
|
|
60
|
+
/** The rules this group contributes to the write axis. */
|
|
61
|
+
readonly write?: RuleGroupAxis;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* What the write-never-exceeds-read invariant removed from the write axis.
|
|
65
|
+
*
|
|
66
|
+
* Reported rather than applied silently: a group that only grants write access
|
|
67
|
+
* to a directory the read axis does not cover grants nothing at all, and a user
|
|
68
|
+
* who configured it must be able to see that instead of concluding the rule was
|
|
69
|
+
* ignored.
|
|
70
|
+
*
|
|
71
|
+
* This value is the ONE answer to "what did the intersection remove", and it is
|
|
72
|
+
* published verbatim to the client half inside the session's stored record
|
|
73
|
+
* (`./session-store.ts`). A browser cannot re-derive it: the path algebra below
|
|
74
|
+
* canonicalizes every root with `realpath` and derives a `workspace` base from
|
|
75
|
+
* `os.tmpdir()`, neither of which exists there. Publishing the resolution's own
|
|
76
|
+
* result is therefore what keeps a surface from claiming a narrowing the fence
|
|
77
|
+
* does not enforce, or missing one it does.
|
|
78
|
+
*/
|
|
79
|
+
export interface WriteNarrowing {
|
|
80
|
+
/** Whether the read axis makes the effective write range strictly smaller. */
|
|
81
|
+
readonly narrowed: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Absolute roots the write axis permitted and the effective range does not.
|
|
84
|
+
* Holds both the write roots the read axis does not cover and the read roots
|
|
85
|
+
* the write range newly excludes; empty when {@link lostUnbounded} alone
|
|
86
|
+
* carries the loss, because an unbounded write axis has no root list to lose.
|
|
87
|
+
*/
|
|
88
|
+
readonly droppedRoots: readonly string[];
|
|
89
|
+
/** Whether an unbounded write axis became bounded, which no root list can state. */
|
|
90
|
+
readonly lostUnbounded: boolean;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The pair actually in force, with every selected group expanded and the write
|
|
94
|
+
* axis already intersected with the read axis. Both members are plain
|
|
95
|
+
* {@link AxisScope} values carrying no group ids, so every consumer resolves
|
|
96
|
+
* them with the same {@link resolveScope} it used before groups existed.
|
|
97
|
+
*/
|
|
98
|
+
export interface EffectiveAxes {
|
|
99
|
+
/** The read range in force. */
|
|
100
|
+
readonly read: AxisScope;
|
|
101
|
+
/** The write range in force, never wider than {@link read}. */
|
|
102
|
+
readonly write: AxisScope;
|
|
103
|
+
/** What the intersection removed from the write axis. */
|
|
104
|
+
readonly narrowing: WriteNarrowing;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* A resolved pair, or the sentence explaining why it could not be resolved.
|
|
108
|
+
* Resolution failure is a value rather than a throw because every caller has a
|
|
109
|
+
* different fail-closed rendering for it: the fence refuses the access, the
|
|
110
|
+
* prompt states that no path is known to be allowed, and `/axis` returns an
|
|
111
|
+
* error without writing an event.
|
|
112
|
+
*/
|
|
113
|
+
export type EffectiveAxesResolution = {
|
|
114
|
+
readonly ok: true;
|
|
115
|
+
readonly axes: EffectiveAxes;
|
|
116
|
+
} | {
|
|
117
|
+
readonly ok: false;
|
|
118
|
+
readonly problem: string;
|
|
119
|
+
};
|
|
120
|
+
/** The narrowing that removed nothing. Exported: it is the stored record's absent value. */
|
|
121
|
+
export declare const NO_NARROWING: WriteNarrowing;
|
|
122
|
+
/**
|
|
123
|
+
* Read one stored narrowing report, or throw naming the member that is
|
|
124
|
+
* unreadable.
|
|
125
|
+
*
|
|
126
|
+
* An absent member is the legal "nothing was ever published for this record"
|
|
127
|
+
* state and yields {@link NO_NARROWING}: that is exactly what the fence enforced
|
|
128
|
+
* before this member existed, so the surface shows no narrowing rather than
|
|
129
|
+
* inventing one. Anything PRESENT must be well formed — a report whose root list
|
|
130
|
+
* cannot be read would otherwise be displayed as "nothing was removed".
|
|
131
|
+
* @param value - the untrusted `narrowing` member of a stored record.
|
|
132
|
+
* @returns the validated report.
|
|
133
|
+
* @throws When the member is present and not a well-formed report.
|
|
134
|
+
*/
|
|
135
|
+
export declare function parseNarrowing(value: unknown): WriteNarrowing;
|
|
136
|
+
/**
|
|
137
|
+
* The group ids a preset references, in first-seen order and without repeats.
|
|
138
|
+
* Callers use this to skip reading the settings page at all for the sessions
|
|
139
|
+
* that reference no groups.
|
|
140
|
+
* @param preset - the session's own axis preset.
|
|
141
|
+
* @returns the referenced ids.
|
|
142
|
+
*/
|
|
143
|
+
export declare function referencedGroupIds(preset: EffectiveScopes): readonly string[];
|
|
144
|
+
/**
|
|
145
|
+
* The ids the settings page's library defines, for validating one command entry
|
|
146
|
+
* before it is recorded.
|
|
147
|
+
*
|
|
148
|
+
* An unusable library yields no ids: every `groups` entry is then refused by
|
|
149
|
+
* name, which is the same fail-closed answer the decision path gives.
|
|
150
|
+
* @param library - the settings page's current `groups` value, untrusted.
|
|
151
|
+
* @returns the defined ids.
|
|
152
|
+
*/
|
|
153
|
+
export declare function ruleGroupIds(library: unknown): ReadonlySet<string>;
|
|
154
|
+
/**
|
|
155
|
+
* Attach the group ids a NEW session starts out referencing to the axes that are
|
|
156
|
+
* `custom`, leaving every other axis exactly as declared.
|
|
157
|
+
*
|
|
158
|
+
* The rule is judged per axis, and only an axis whose OWN kind is `custom` can
|
|
159
|
+
* carry a reference: checking the settings row instead (a tick that is not
|
|
160
|
+
* tied to an axis) promoted `read: all` to `read: { kind: 'custom', base: 'all' }`
|
|
161
|
+
* even though no axis was ever set to custom. An axis of one of the three closed
|
|
162
|
+
* kinds has nowhere to put an id and is seeded verbatim — promoting it would
|
|
163
|
+
* report a custom scope the settings page never showed.
|
|
164
|
+
*
|
|
165
|
+
* A group may carry rules for either axis or both, and the session selected it
|
|
166
|
+
* once, so the same list goes on both `custom` axes; the group's `read` rules
|
|
167
|
+
* then apply where they exist and its `write` rules where they exist. Two axes
|
|
168
|
+
* are therefore independent: one may take the references while the other keeps
|
|
169
|
+
* its closed kind.
|
|
170
|
+
* @param axes - the pair a new session is seeded with.
|
|
171
|
+
* @param ids - the settings page's `defaultGroups`.
|
|
172
|
+
* @returns the pair carrying those references.
|
|
173
|
+
*/
|
|
174
|
+
export declare function seedGroupReferences(axes: EffectiveScopes, ids: readonly string[]): EffectiveScopes;
|
|
175
|
+
/**
|
|
176
|
+
* The intersection of two evaluated ranges, as an evaluated range. Exact for
|
|
177
|
+
* canonical directory roots: two roots are either nested — the deeper one is
|
|
178
|
+
* their intersection — or disjoint, and a union of directories intersected with
|
|
179
|
+
* a union of directories is the union of those pairwise intersections.
|
|
180
|
+
* @param left - one evaluated range.
|
|
181
|
+
* @param right - the other evaluated range.
|
|
182
|
+
* @returns the range permitting exactly what both permit.
|
|
183
|
+
*/
|
|
184
|
+
export declare function intersectResolved(left: ResolvedScope, right: ResolvedScope): ResolvedScope;
|
|
185
|
+
/**
|
|
186
|
+
* Spell an evaluated range as an axis value that evaluates back to it. The
|
|
187
|
+
* `deny` base plus the roots themselves is the exact spelling: a `workspace`
|
|
188
|
+
* base would re-add the platform temp areas this range may never have had.
|
|
189
|
+
* @param resolved - the evaluated range.
|
|
190
|
+
* @returns the equivalent axis value.
|
|
191
|
+
*/
|
|
192
|
+
export declare function scopeOfResolved(resolved: ResolvedScope): AxisScope;
|
|
193
|
+
/**
|
|
194
|
+
* THE resolution: one session's axis preset plus the current group library into
|
|
195
|
+
* the read and write ranges actually in force.
|
|
196
|
+
*
|
|
197
|
+
* The write member of the result is the intersection of the write axis's range
|
|
198
|
+
* with the read axis's. The write axis keeps its own spelling whenever the
|
|
199
|
+
* intersection changed nothing, so a session that references no groups and needs
|
|
200
|
+
* no narrowing is described and enforced exactly as before groups existed.
|
|
201
|
+
*
|
|
202
|
+
* An unresolvable reference or an unusable library returns `ok: false` and no
|
|
203
|
+
* axes: the caller refuses, because resolving a missing id to an empty group
|
|
204
|
+
* would silently drop rules the user configured.
|
|
205
|
+
* @param preset - the session's own axis preset, group ids included.
|
|
206
|
+
* @param library - the settings page's current `groups` value, untrusted.
|
|
207
|
+
* @param policy - the workspace root a `workspace` base resolves against.
|
|
208
|
+
* @returns the effective pair, or the sentence explaining why there is none.
|
|
209
|
+
*/
|
|
210
|
+
export declare function resolveEffectiveAxes(preset: EffectiveScopes, library: unknown, policy: ScopePolicy): EffectiveAxesResolution;
|
|
211
|
+
/**
|
|
212
|
+
* The narrower of two bases, in the containment order `deny` ⊂ `workspace` ⊂
|
|
213
|
+
* `all`.
|
|
214
|
+
*
|
|
215
|
+
* This is the tier the write-never-exceeds-read invariant can express to the
|
|
216
|
+
* layer BELOW the tool layer. That layer is a single closed sandbox mode
|
|
217
|
+
* mirrored from the write axis onto the session's `sandbox/mode`, and it cannot
|
|
218
|
+
* spell a path list; the narrower base is the largest tier both axes contain, so
|
|
219
|
+
* mirroring it keeps the operating-system confinement inside the intersection
|
|
220
|
+
* instead of leaving it at the write axis's own, wider base.
|
|
221
|
+
* @param left - one axis.
|
|
222
|
+
* @param right - the other axis.
|
|
223
|
+
* @returns the narrower base.
|
|
224
|
+
*/
|
|
225
|
+
export declare function narrowerBase(left: AxisScope, right: AxisScope): 'deny' | 'workspace' | 'all';
|
|
226
|
+
//# sourceMappingURL=groups.d.ts.map
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 双轴访问模式组合包(宿主半边):把文件沙箱的两条访问轴 —— 读轴与写轴,
|
|
3
|
+
* 每条都取 deny | workspace | all | custom,custom 形如
|
|
4
|
+
* `{ kind: 'custom', base, allow[], deny[] }`,语义是 allow = base ∪ allow − deny
|
|
5
|
+
* 且 deny 优先 —— 作为一份可安装、可配置的组合层暴露出去。
|
|
6
|
+
*
|
|
7
|
+
* 这一层持有两件事:新会话从哪两条轴开始(本行的 Loader entry Config,见
|
|
8
|
+
* `./config.ts`),以及这两条轴在一条会话里怎么存、怎么读
|
|
9
|
+
* (`./session-store.ts`,本包自有的设置命名空间 `dual-axis-sessions`,按会话 id
|
|
10
|
+
* 分区)。它把前者下发到后者的时机是 `ctx.on('session/created')`:0.1.7 里
|
|
11
|
+
* `packages/interaction/permission-presets/src/index.ts:245` 用同一个钩子钉
|
|
12
|
+
* 权限预设,本包在它之后注册,因此新会话先拿到预设的 mode,再被本包写上一对轴。
|
|
13
|
+
* 那一对轴按会话来源分两种:顶层会话取设置页那一行当刻的值,子代理子会话取
|
|
14
|
+
* **父会话当刻生效的那一对**(见 `./session-store.ts` 的 `inheritedAxes`)。
|
|
15
|
+
* 同一个种子也是**记录缺席时那条会话被修复成的取值**(`seedAxesFor`),修复由
|
|
16
|
+
* 三处活读路径上的 `store.ensure` 触发,见该方法的说明。
|
|
17
|
+
*
|
|
18
|
+
* 为什么轴不在会话日志里(0.1.7 上游删改所致,逐条依据见 README):
|
|
19
|
+
* - 轴代数由 `./axis.ts` 与 `./scope.ts` 自带(0.1.7 全删)。
|
|
20
|
+
* - 会话内的载体由本包自己声明的事件类型承担在 0.1.7 里**不成立**:该类型不在
|
|
21
|
+
* `KNOWN_SESSION_EVENT_TYPES` 里,而 `Session.append` 的信封硬编码、不接
|
|
22
|
+
* `ignorable`,于是写过的会话会被 `validateStoredEvents` 整份拒收、冷启动后
|
|
23
|
+
* 打不开。轴因此搬到 `./session-store.ts`;日志里只保留写轴基准档经
|
|
24
|
+
* `sandbox/mode` 的那一次镜像(`./session-axes.ts`)。
|
|
25
|
+
* - 读路径围栏由 `./fs-fence.ts` 自己实现(0.1.7 的 fs-sandbox 只围栏写)。
|
|
26
|
+
* - 设置节由本行的 Config + `.volatile()` 承载(0.1.7 删了 `installSection`
|
|
27
|
+
* 与任意命名空间注册,ns 就是 entry id)。
|
|
28
|
+
*
|
|
29
|
+
* 客户端半边在 `@t4r71/dsh-dual-axis-ui`:本包不声明 `dsh.client`。
|
|
30
|
+
*
|
|
31
|
+
* @module @t4r71/dsh-dual-axis
|
|
32
|
+
*/
|
|
33
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
34
|
+
import type { Config } from './config.ts';
|
|
35
|
+
export { DUAL_AXIS_ROW_ID, RETIRED_SETTINGS_NAMESPACE, Config, axesOf, declaredSection, defaultGroupIds, groupLibraryValue, normalizeScope, sameScope, } from './config.ts';
|
|
36
|
+
export type { Config as DualAxisConfig } from './config.ts';
|
|
37
|
+
export { intersectResolved, narrowerBase, referencedGroupIds, resolveEffectiveAxes, ruleGroupIds, scopeOfResolved, seedGroupReferences, } from './groups.ts';
|
|
38
|
+
export type { EffectiveAxes, EffectiveAxesResolution, RuleGroup, RuleGroupAxis, WriteNarrowing, } from './groups.ts';
|
|
39
|
+
export { mirrorWriteMode } from './session-axes.ts';
|
|
40
|
+
export { AXES_WRITE_ATTEMPTS, AXES_WRITE_BACKOFF_MS, Config as SessionAxesConfig, DEFAULT_AXES, SESSION_AXES_FIELD, SESSION_AXES_NAMESPACE, SessionAxesConflictError, SessionAxesStore, parseSessionAxesField, parseSessionAxesRecord, seedAxesFor, seedPair, sessionAxesStore, } from './session-store.ts';
|
|
41
|
+
export type { SessionAxesField, SessionAxesRecord, SessionAxesStoreOptions, SessionAxesSweep, SessionAxesWriter, } from './session-store.ts';
|
|
42
|
+
export { AXIS_COMMAND_NAME, registerAxisCommand } from './axis-command.ts';
|
|
43
|
+
export { parseAxisEntry } from './axis-entry.ts';
|
|
44
|
+
export type { AxisEntryParse } from './axis-entry.ts';
|
|
45
|
+
export { AXIS_BASES, AXIS_KINDS, DEFAULT_READ_SCOPE, DEFAULT_WRITE_SCOPE, effectiveScopes, isModeConsistent, isSandboxMode, modeOfAxisBase, modeOfScope, scopeOfMode, } from './axis.ts';
|
|
46
|
+
export type { AxisBase, AxisScope, AxisScopeKind, EffectiveScopes as EffectiveScopesType } from './axis.ts';
|
|
47
|
+
export { ScopeConfigError, isAbsoluteSpelling, isLexicallyUnder, resolveScope, scopeContains } from './scope.ts';
|
|
48
|
+
export type { ResolvedScope, ScopePolicy } from './scope.ts';
|
|
49
|
+
export { DualAxisFileSystem, readAxisRefusal } from './fs-fence.ts';
|
|
50
|
+
export type { ReadAxes } from './fs-fence.ts';
|
|
51
|
+
export { NO_BYPASS, WIDENING_EXIT, renderAxisRange, renderScopePrompt, scopeRefusal } from './scope-prompt.ts';
|
|
52
|
+
export type { ScopeRefusal } from './scope-prompt.ts';
|
|
53
|
+
/**
|
|
54
|
+
* 双轴访问模式组合层。
|
|
55
|
+
*
|
|
56
|
+
* 挂载时做四件事:自检行里声明的轴形状、在会话创建钩子上给新会话钉一对轴、
|
|
57
|
+
* 按需回收已经消失的会话的记录、把写轴基准档镜像进日志。改完配置只管之后的
|
|
58
|
+
* 会话;已经在跑的会话保持它存储里那一对轴。子代理子会话不取设置页的值,
|
|
59
|
+
* 取它的父会话当刻那一对。
|
|
60
|
+
*/
|
|
61
|
+
export declare class DualAxis {
|
|
62
|
+
/**
|
|
63
|
+
* 这一行需要的能力:会话表(补齐挂载前的会话)。依赖是静态字段,Cordis 的
|
|
64
|
+
* loader 只从插件的 `inject` 属性读它 —— 函数形态导出同名常量不会被采用。
|
|
65
|
+
*/
|
|
66
|
+
static inject: readonly ["sessions"];
|
|
67
|
+
/** 本行的组合配置 schema,由 loader 在挂载前校验。 */
|
|
68
|
+
static Config: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
|
|
69
|
+
read: import("@deepseek-ai/schemastery").default<any, any, "volatile">;
|
|
70
|
+
write: import("@deepseek-ai/schemastery").default<any, any, "volatile">;
|
|
71
|
+
groups: import("@deepseek-ai/schemastery").default<NoInfer<any[]>, NoInfer<any[]>, "volatile-defined">;
|
|
72
|
+
defaultGroups: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
73
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
74
|
+
read: import("@deepseek-ai/schemastery").default<any, any, "volatile">;
|
|
75
|
+
write: import("@deepseek-ai/schemastery").default<any, any, "volatile">;
|
|
76
|
+
groups: import("@deepseek-ai/schemastery").default<NoInfer<any[]>, NoInfer<any[]>, "volatile-defined">;
|
|
77
|
+
defaultGroups: import("@deepseek-ai/schemastery").default<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
78
|
+
}>>, "plain">;
|
|
79
|
+
/**
|
|
80
|
+
* 本行的组合配置,挂载那一刻的那一份。
|
|
81
|
+
*
|
|
82
|
+
* 两条轴都是 `.volatile()` 字段,取值是 schema 的 accessor;但这份 accessor
|
|
83
|
+
* 是**构造期**解析出来的,0.1.7 的 loader 不把后来的设置改动提交回来,所以它
|
|
84
|
+
* 只作 {@link declaredAxes} 取不到设置服务时的兜底,不再是新会话的取值来源。
|
|
85
|
+
*/
|
|
86
|
+
private readonly config;
|
|
87
|
+
/** 轴的权威存储:本包自有的设置命名空间,按会话 id 分区。 */
|
|
88
|
+
private readonly store;
|
|
89
|
+
/**
|
|
90
|
+
* @param ctx - 宿主上下文,本层读会话表、注册会话钩子、写轴存储都经它。
|
|
91
|
+
* @param config - 本行的组合配置(挂载那一刻的值)。
|
|
92
|
+
*/
|
|
93
|
+
constructor(ctx: Context, config: Config);
|
|
94
|
+
/**
|
|
95
|
+
* 给一条会话钉上它该有的两条轴。
|
|
96
|
+
*
|
|
97
|
+
* 取值来源按会话来源分叉:子代理子会话继承父会话当刻那一对,其余会话取设置页
|
|
98
|
+
* 那一行当刻的值。这条会话在存储里已经有记录时**一律不写**:那条记录就是这条会话
|
|
99
|
+
* 自己的轴,设置页此后再改与它无关,而每一次重钉都会把用户在下拉里选过的值按设置页
|
|
100
|
+
* 覆盖回去。
|
|
101
|
+
*
|
|
102
|
+
* **没有内容**的会话同样**不写**:不是「已经有记录」,而是「还不该有记录」。
|
|
103
|
+
* 工作区选择器在同一个工作区里开新对话时复用同一条会话 id(`docs` 与 README 里
|
|
104
|
+
* 记着这条实测),所以创建即冻结等于把一条还没人用过的会话按当天的设置页钉死;
|
|
105
|
+
* 这条会话此后每次读取都由 `ensure` 现算种子,设置页改一行它就跟着改,直到它真的
|
|
106
|
+
* 被用过。落记录的两个时机因此是:**这一条会话被用过**(`hasContent`),或者
|
|
107
|
+
* **用户经 `/axis` 手动改过它的轴**(那条写路径无条件落,见 `registerAxisCommand`)。
|
|
108
|
+
* @param ctx - 宿主上下文,用于读这条会话与它父会话的轴。
|
|
109
|
+
* @param session - 目标会话。
|
|
110
|
+
*/
|
|
111
|
+
private pin;
|
|
112
|
+
/**
|
|
113
|
+
* 一条会话该有的那一对轴 —— 新会话的种子,也是记录缺席时被修复成的取值。
|
|
114
|
+
*
|
|
115
|
+
* `pin` 与三处活读路径上的 `store.ensure` 走的是这同一个方法,所以「新建时写下的」
|
|
116
|
+
* 与「后来补写的」不可能算出两个答案;没有设置服务的组合里用本行自己的 Config 兜底。
|
|
117
|
+
* @param ctx - 宿主上下文。
|
|
118
|
+
* @param session - 目标会话。
|
|
119
|
+
* @returns 这一对轴。
|
|
120
|
+
*/
|
|
121
|
+
private seedFor;
|
|
122
|
+
/**
|
|
123
|
+
* 这条会话当刻生效的那一对轴 —— 记录缺席时现算种子,有记录时读记录;两条轴都按
|
|
124
|
+
* 引用的 id **当刻**取组定义,再求写轴 ∩ 读轴。
|
|
125
|
+
*
|
|
126
|
+
* 提示词、围栏与客户端显示走的是这**同一处**:解算结果里的 `narrowing` 被原样发布进
|
|
127
|
+
* 这条会话的记录({@link SessionAxesStore.refreshNarrowing}),于是界面上说「被削掉了
|
|
128
|
+
* 这些根」与实际执行的围栏不会有第二个答案 —— 浏览器半边既算不出 `workspace` 底座
|
|
129
|
+
* (要 `os.tmpdir()`),也算不出根路径的规范拼写(要 `realpath`)。
|
|
130
|
+
* @param ctx - 宿主上下文。
|
|
131
|
+
* @param session - 目标会话。
|
|
132
|
+
* @param workspaceRoot - 这条会话的工作区根,`workspace` 与 `custom` 底座对它的解析。
|
|
133
|
+
* @returns 生效的一对轴,或解算不出来的原因。
|
|
134
|
+
*/
|
|
135
|
+
private resolveFor;
|
|
136
|
+
/**
|
|
137
|
+
* 按需回收:存储里那些 id 已经不在持久化会话表里的记录。
|
|
138
|
+
*
|
|
139
|
+
* 起停各扫一次、此后每 {@link SWEEP_INTERVAL_MS} 一次,都是后台任务:清扫失败只记
|
|
140
|
+
* 日志,绝不影响会话创建或判定路径。判据(为什么不是 `session/disposed`)见
|
|
141
|
+
* {@link SessionAxesStore.sweep}。
|
|
142
|
+
* @param ctx - 宿主上下文。
|
|
143
|
+
*/
|
|
144
|
+
/**
|
|
145
|
+
* 把每一条在册会话的收窄结论重新解算并发一次。
|
|
146
|
+
*
|
|
147
|
+
* 为什么需要这一步:收窄结论是解算的产物,而解算的输入里就有**组定义**,改一个组的
|
|
148
|
+
* 规则只改设置文档、不碰任何一条会话。少了这一次重发,界面上那句「读轴削掉了这些根」
|
|
149
|
+
* 就要等到这条会话下一次被触达(下一轮提示词、下一次工具调用、下一次改轴)才跟上,
|
|
150
|
+
* 而人在设置页改完组之后回到对话里看下拉时,两者都还没发生。
|
|
151
|
+
*
|
|
152
|
+
* 判定路径不受影响:围栏与提示词一直是按 id **现取**组定义的,这里重发的只是那份
|
|
153
|
+
* 已经解算出来的结论。没有记录的会话跳过 —— 它的轴本来就每次现算,落一份反而会冻住。
|
|
154
|
+
* @param ctx - 宿主上下文。
|
|
155
|
+
*/
|
|
156
|
+
private republishNarrowing;
|
|
157
|
+
private sweepSchedule;
|
|
158
|
+
/**
|
|
159
|
+
* 设置页那一行当刻声明的两条轴。
|
|
160
|
+
*
|
|
161
|
+
* 走 `ctx.settings.describe()` 而不是构造期捕获的 `this.config`:设置页保存写的
|
|
162
|
+
* 是 Loader entry 的 config 文档,而 `this.config` 是挂载那一刻解析出来的
|
|
163
|
+
* volatile accessor 快照。0.1.7 的 loader 不把新值提交回插件实例持有的那一份
|
|
164
|
+
* (`vendor/loader/src/config/entry.ts:162-195`),所以同一进程内保存后,读
|
|
165
|
+
* `this.config` 拿到的仍是旧值,必须重启才生效。`describe()` 每次都从
|
|
166
|
+
* `entry.fiber.config` 重新投影并解 volatile
|
|
167
|
+
* (`packages/settings/settings/src/index.ts:319-323` +
|
|
168
|
+
* `packages/settings/settings/src/schema.ts:10-17`),与设置页摘要显示用的是
|
|
169
|
+
* 同一条读路径,因此这里读到的就是文件里那份值。
|
|
170
|
+
*
|
|
171
|
+
* `settings` 不在场的组合(本包可在没有设置服务的宿主里单独装载)退回构造期
|
|
172
|
+
* 那一份,也就是没有设置页可保存时的唯一取值。
|
|
173
|
+
* @param ctx - 宿主上下文。
|
|
174
|
+
* @returns 两条轴。
|
|
175
|
+
*/
|
|
176
|
+
private declaredAxes;
|
|
177
|
+
/**
|
|
178
|
+
* 设置页声明「新会话默认加载」的组 id。
|
|
179
|
+
*
|
|
180
|
+
* 只被 {@link pin} 用来播种新会话,判定路径一概不读它 —— 与 read/write 两个默认值同理,
|
|
181
|
+
* 它描述的是一条**将要建立**的会话,不是任何已经有记录的会话。
|
|
182
|
+
* @param ctx - 宿主上下文。
|
|
183
|
+
* @returns 声明的组 id;没有设置服务时为空。
|
|
184
|
+
*/
|
|
185
|
+
private declaredDefaultGroups;
|
|
186
|
+
}
|
|
187
|
+
/** 首次清扫的延迟:等 loader 把每个入口都激活、持久化后端也装好。 */
|
|
188
|
+
export declare const SWEEP_FIRST_DELAY_MS = 15000;
|
|
189
|
+
/** 之后每次清扫的间隔。 */
|
|
190
|
+
export declare const SWEEP_INTERVAL_MS = 30000;
|
|
191
|
+
/**
|
|
192
|
+
* 两次重发收窄结论之间的毫秒数 —— 界面上那句「读轴削掉了这些根」跟随组定义的最坏延迟。
|
|
193
|
+
*
|
|
194
|
+
* 比回收记录的间隔短:回收只是清理,而这句话是给人读的判断,而它唯一的其他更新时机是
|
|
195
|
+
* 这条会话被触达(下一轮提示词、下一次工具调用、下一次改轴)—— 人在设置页改完组之后回到
|
|
196
|
+
* 对话里看下拉时,这三件事都还没发生。重发本身只在结论真的变了时才写文档。
|
|
197
|
+
*/
|
|
198
|
+
export declare const NARROWING_REFRESH_MS = 10000;
|
|
199
|
+
/**
|
|
200
|
+
* 子代理子会话继承父会话当刻那一对轴 —— 定义搬到了 `./session-store.ts`,
|
|
201
|
+
* 因为「记录缺席时的修复写」要用同一份计算。此处再导出,保持本包既有的公开面。
|
|
202
|
+
*/
|
|
203
|
+
export { inheritedAxes } from './session-store.ts';
|
|
204
|
+
export default DualAxis;
|
|
205
|
+
//# sourceMappingURL=index.d.ts.map
|