@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,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dual-axis bundle's command registration: the one write path the two
|
|
3
|
+
* composer dropdowns reach the host through.
|
|
4
|
+
*
|
|
5
|
+
* The name is NOT permission. @deepseek-ai/dsh-permission-presets registers
|
|
6
|
+
* /permission on the global command layer, and the registry's NamedEntries
|
|
7
|
+
* throws on a duplicate global name, so reusing it would fail this plugin's
|
|
8
|
+
* load. The preset picker keeps /permission; this command owns the axes.
|
|
9
|
+
*
|
|
10
|
+
* Registration rides ctx.inject(['commands'], …) rather than the plugin's
|
|
11
|
+
* static inject: a profile without a command registry composes this bundle
|
|
12
|
+
* for its fence and prompt, and an unconditional ctx.commands access would fail
|
|
13
|
+
* that load.
|
|
14
|
+
*
|
|
15
|
+
* The write is a WHOLE-DOCUMENT settings write and therefore asynchronous, so
|
|
16
|
+
* the handler is too. A lost revision race settles as an ordinary command
|
|
17
|
+
* error carrying the conflict's own sentence: the dispatching surface renders
|
|
18
|
+
* it beside the picker, and nothing is reported as applied that was not stored.
|
|
19
|
+
*
|
|
20
|
+
* @module @t4r71/dsh-dual-axis/axis-command
|
|
21
|
+
*/
|
|
22
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
23
|
+
import type { Session } from '@deepseek-ai/dsh-session';
|
|
24
|
+
import type { EffectiveScopes } from './axis.ts';
|
|
25
|
+
/** The registered command name, without its leading slash. */
|
|
26
|
+
export declare const AXIS_COMMAND_NAME = "axis";
|
|
27
|
+
/** What the registration needs from its owner. */
|
|
28
|
+
export interface AxisCommandOptions {
|
|
29
|
+
/**
|
|
30
|
+
* The session's standing axis pair, read from the session axis store — the one
|
|
31
|
+
* source every other consumer reads. Supplies the axis a one-axis entry does
|
|
32
|
+
* not name.
|
|
33
|
+
* @param session - the session the invocation belongs to.
|
|
34
|
+
* @returns the pair in force, with both defaults filled in.
|
|
35
|
+
*/
|
|
36
|
+
readonly current: (session: Session) => EffectiveScopes;
|
|
37
|
+
/**
|
|
38
|
+
* The rule-group ids the settings page currently defines. Read at the moment
|
|
39
|
+
* of the invocation so a group created since startup is selectable, and used
|
|
40
|
+
* only to REFUSE an unknown id: an existing id is recorded verbatim and
|
|
41
|
+
* expanded at the moment of use, never here.
|
|
42
|
+
* @returns the defined ids.
|
|
43
|
+
*/
|
|
44
|
+
readonly knownGroups: () => ReadonlySet<string>;
|
|
45
|
+
/**
|
|
46
|
+
* Store one session's new pair. Supplied by the composing plugin, which owns
|
|
47
|
+
* the store; the command layer never reaches the settings document itself.
|
|
48
|
+
* @param session - the session to write.
|
|
49
|
+
* @param axes - the complete new pair.
|
|
50
|
+
*/
|
|
51
|
+
readonly write: (session: Session, axes: EffectiveScopes) => Promise<void>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Register the axis command for every composed command adapter.
|
|
55
|
+
* @param ctx - host context; the registration is owned by this context's fiber.
|
|
56
|
+
* @param options - the standing-pair reader, the group-id reader, and the write path.
|
|
57
|
+
*/
|
|
58
|
+
export declare function registerAxisCommand(ctx: Context, options: AxisCommandOptions): void;
|
|
59
|
+
//# sourceMappingURL=axis-command.d.ts.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dual-axis command's argument grammar: one client-composed entry string
|
|
3
|
+
* into one access axis.
|
|
4
|
+
*
|
|
5
|
+
* The grammar is owned by the browser half's pickers, which emit exactly two
|
|
6
|
+
* forms — <axis>:<deny|workspace|all> for the three closed values, and
|
|
7
|
+
* <axis>:custom:base=<deny|workspace|all>[,groups=<id>|<id>][,allow=<absolute path>][,deny=<absolute path>]
|
|
8
|
+
* from the path editor, where a repeated key carries several entries.
|
|
9
|
+
*
|
|
10
|
+
* A `groups` entry records the IDS ONLY, checked against the ids the settings
|
|
11
|
+
* page currently defines and then stored verbatim. The group's rules are never
|
|
12
|
+
* expanded into the entry: expansion happens at the moment of use, so editing a
|
|
13
|
+
* group reaches every session that references it without a restart.
|
|
14
|
+
*
|
|
15
|
+
* The caller supplies the session's standing pair so a one-axis entry returns
|
|
16
|
+
* the complete new pair; the axis the entry does not name keeps its value.
|
|
17
|
+
*
|
|
18
|
+
* @module @t4r71/dsh-dual-axis/axis-entry
|
|
19
|
+
*/
|
|
20
|
+
import type { EffectiveScopes } from './axis.ts';
|
|
21
|
+
/** The two axis names an entry string may start with. */
|
|
22
|
+
export declare const AXIS_ENTRY_NAMES: readonly string[];
|
|
23
|
+
/** A parsed entry, or the sentence explaining why it is not one. */
|
|
24
|
+
export type AxisEntryParse = {
|
|
25
|
+
readonly ok: true;
|
|
26
|
+
readonly axes: EffectiveScopes;
|
|
27
|
+
} | {
|
|
28
|
+
readonly ok: false;
|
|
29
|
+
readonly problem: string;
|
|
30
|
+
};
|
|
31
|
+
/** The separator between rule-group ids in one `groups` entry. */
|
|
32
|
+
export declare const GROUP_ID_SEPARATOR = "|";
|
|
33
|
+
/**
|
|
34
|
+
* Read one client-composed axis entry into the complete axis pair it selects.
|
|
35
|
+
* @param rawInput - the command invocation's verbatim input; the commands
|
|
36
|
+
* registry passes the text after the command name including the separating
|
|
37
|
+
* space, so this trims first.
|
|
38
|
+
* @param current - the session's standing pair; the axis the entry does not
|
|
39
|
+
* name keeps its value from here.
|
|
40
|
+
* @param knownGroups - the rule-group ids the settings page currently defines;
|
|
41
|
+
* a `groups` entry naming anything else is refused.
|
|
42
|
+
* @returns the new pair, or the sentence explaining why the entry was refused.
|
|
43
|
+
*/
|
|
44
|
+
export declare function parseAxisEntry(rawInput: string, current: EffectiveScopes, knownGroups?: ReadonlySet<string>): AxisEntryParse;
|
|
45
|
+
//# sourceMappingURL=axis-entry.d.ts.map
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The read/write ACCESS AXES of the file sandbox: the per-axis vocabulary a
|
|
3
|
+
* session selects and the lossless mapping onto the legacy single `mode`.
|
|
4
|
+
*
|
|
5
|
+
* This is the 0.1.7 port of the algebra upstream deleted. 0.1.6 shipped it as
|
|
6
|
+
* `@deepseek-ai/dsh-sandbox/access` (152 lines) and
|
|
7
|
+
* `@deepseek-ai/dsh-sandbox/scope` (152 lines); 0.1.7 has neither symbol nor
|
|
8
|
+
* file, so the package carries its own copy and keeps the 0.1.6 semantics
|
|
9
|
+
* verbatim: four kinds, `custom` = base + allow - deny with deny winning.
|
|
10
|
+
*
|
|
11
|
+
* One axis answers "which absolute paths may this execution touch". `deny`
|
|
12
|
+
* permits nothing, `workspace` permits the calling session's workspace root
|
|
13
|
+
* plus the platform temp areas, `all` is the whole host filesystem, and
|
|
14
|
+
* `custom` names a BASE kind plus absolute path ADDITIONS and REMOVALS —
|
|
15
|
+
* path lists, not patterns.
|
|
16
|
+
*
|
|
17
|
+
* `mode` REMAINS the write axis's persistence spelling. The session-format
|
|
18
|
+
* whitelist pins its literal values, so this module keeps `mode` derivable
|
|
19
|
+
* from the write scope in both directions instead of replacing it:
|
|
20
|
+
* {@link scopeOfMode} and {@link modeOfScope} are the two halves of that
|
|
21
|
+
* bijection, and `custom` maps onto the mode implied by its own base.
|
|
22
|
+
*
|
|
23
|
+
* @module @t4r71/dsh-dual-axis/axis
|
|
24
|
+
*/
|
|
25
|
+
import type { SandboxExecutionPolicy, SandboxMode } from '@deepseek-ai/dsh-sandbox';
|
|
26
|
+
/**
|
|
27
|
+
* One axis's kind. `deny` touches nothing; `workspace` is the session's
|
|
28
|
+
* workspace plus the platform temp areas; `all` is the host filesystem;
|
|
29
|
+
* `custom` carries additions and removals over a `base`.
|
|
30
|
+
*/
|
|
31
|
+
export type AxisScopeKind = 'deny' | 'workspace' | 'all' | 'custom';
|
|
32
|
+
/** The comparison base a `custom` scope adds to and removes from. */
|
|
33
|
+
export type AxisBase = 'deny' | 'workspace' | 'all';
|
|
34
|
+
/**
|
|
35
|
+
* One axis's complete value. The three closed kinds carry no payload;
|
|
36
|
+
* `custom` carries the absolute paths its selection adds (`allow`) and the
|
|
37
|
+
* absolute paths it removes even from the base (`deny`). Removal wins over
|
|
38
|
+
* addition — see {@link scopeContains}.
|
|
39
|
+
*/
|
|
40
|
+
export type AxisScope = {
|
|
41
|
+
kind: Exclude<AxisScopeKind, 'custom'>;
|
|
42
|
+
} | {
|
|
43
|
+
kind: 'custom';
|
|
44
|
+
/** The scope the additions and removals are applied over. */
|
|
45
|
+
base: AxisBase;
|
|
46
|
+
/**
|
|
47
|
+
* Ids of the rule groups this axis references. The DEFINITION of an id is
|
|
48
|
+
* read from the settings page at the moment of use, never stored here: a
|
|
49
|
+
* session that copied a group's contents would keep enforcing the copy after
|
|
50
|
+
* the group changed, and copies of one group drift apart.
|
|
51
|
+
*/
|
|
52
|
+
groups?: readonly string[] | undefined;
|
|
53
|
+
/** Absolute paths added to the base. */
|
|
54
|
+
allow: readonly string[];
|
|
55
|
+
/** Absolute paths removed from (base ∪ allow). */
|
|
56
|
+
deny: readonly string[];
|
|
57
|
+
};
|
|
58
|
+
/** Every {@link AxisScopeKind}, for option advertisement and untrusted-value validation. */
|
|
59
|
+
export declare const AXIS_KINDS: readonly AxisScopeKind[];
|
|
60
|
+
/** Every {@link AxisBase}, for option advertisement and untrusted-value validation. */
|
|
61
|
+
export declare const AXIS_BASES: readonly AxisBase[];
|
|
62
|
+
/** The default READ axis: every mode permitted reading before the axes existed, so the whole host. */
|
|
63
|
+
export declare const DEFAULT_READ_SCOPE: AxisScope;
|
|
64
|
+
/** The default WRITE axis: the session workspace plus the platform temp areas. */
|
|
65
|
+
export declare const DEFAULT_WRITE_SCOPE: AxisScope;
|
|
66
|
+
/**
|
|
67
|
+
* Whether an untrusted runtime value names one of the three `SandboxMode` members.
|
|
68
|
+
*
|
|
69
|
+
* The TYPE says `SandboxMode`; the session LOG says whatever a past writer put
|
|
70
|
+
* there. A replayed or foreign log can carry a mode this build never wrote, and
|
|
71
|
+
* a fold that trusts it would take the whole projected axis cell down with it.
|
|
72
|
+
* This guard is the boundary that turns 'the log claims a mode' into 'the log
|
|
73
|
+
* claims a mode this build understands': callers read a false answer as 'names
|
|
74
|
+
* no mode' and keep the axis value they already had.
|
|
75
|
+
* @param value - the untrusted value from a session-log payload.
|
|
76
|
+
* @returns true when the value is a `SandboxMode`.
|
|
77
|
+
*/
|
|
78
|
+
export declare function isSandboxMode(value: unknown): value is SandboxMode;
|
|
79
|
+
/**
|
|
80
|
+
* The write scope a legacy `mode` means.
|
|
81
|
+
* @param mode - the sandbox mode.
|
|
82
|
+
* @returns the equivalent write scope.
|
|
83
|
+
*/
|
|
84
|
+
export declare function scopeOfMode(mode: SandboxMode): AxisScope;
|
|
85
|
+
/**
|
|
86
|
+
* The mode an axis BASE implies — the shared half of {@link modeOfScope}. The
|
|
87
|
+
* three-value mode set is closed, so every base spells exactly one mode.
|
|
88
|
+
* @param base - the axis base to spell.
|
|
89
|
+
* @returns the mode that base has always been persisted as.
|
|
90
|
+
*/
|
|
91
|
+
export declare function modeOfAxisBase(base: AxisBase): SandboxMode;
|
|
92
|
+
/**
|
|
93
|
+
* The legacy `mode` a write scope is spelled as. `custom` resolves through its
|
|
94
|
+
* own base: the mode names the containment the fence must not exceed, while the
|
|
95
|
+
* scope's additions and removals ride beside it.
|
|
96
|
+
* @param scope - the write axis value.
|
|
97
|
+
* @returns the mode that spells this scope's base.
|
|
98
|
+
*/
|
|
99
|
+
export declare function modeOfScope(scope: AxisScope): SandboxMode;
|
|
100
|
+
/** The axis pair actually in force, with both defaults filled in. */
|
|
101
|
+
export interface EffectiveScopes {
|
|
102
|
+
/** The read axis in force. */
|
|
103
|
+
read: AxisScope;
|
|
104
|
+
/** The write axis in force. */
|
|
105
|
+
write: AxisScope;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Read the axis pair a resolved policy carries, falling back to the defaults.
|
|
109
|
+
* The policy's `mode` is authoritative for the write axis's BASE: a policy
|
|
110
|
+
* that carries no write axes writes exactly what its mode always meant, so
|
|
111
|
+
* every pre-existing policy keeps its exact behavior.
|
|
112
|
+
*
|
|
113
|
+
* 0.1.7's `SandboxExecutionPolicy` has no `readScope` / `writeScope`
|
|
114
|
+
* members (only `mode`, `workspaceRoot`, `sessionId?`), so the axes arrive
|
|
115
|
+
* through the extra argument this package's own fence passes. The
|
|
116
|
+
* `mode`-derived fallback keeps the function total for the upstream type.
|
|
117
|
+
* @param policy - the resolved policy (supplies `mode`).
|
|
118
|
+
* @param axes - the session's axis pair, when the caller holds one.
|
|
119
|
+
* @returns both axes in force.
|
|
120
|
+
*/
|
|
121
|
+
export declare function effectiveScopes(policy: SandboxExecutionPolicy, axes?: Partial<EffectiveScopes>): EffectiveScopes;
|
|
122
|
+
/**
|
|
123
|
+
* Whether a policy's `mode` still spells its write scope's base — the
|
|
124
|
+
* invariant every construction site must preserve. A custom write scope whose
|
|
125
|
+
* base disagrees with `mode` would enforce containment the session never chose.
|
|
126
|
+
* @param policy - the policy to check.
|
|
127
|
+
* @param axes - the session's axis pair, when the caller holds one.
|
|
128
|
+
* @returns true when the two agree.
|
|
129
|
+
*/
|
|
130
|
+
export declare function isModeConsistent(policy: SandboxExecutionPolicy, axes?: Partial<EffectiveScopes>): boolean;
|
|
131
|
+
//# sourceMappingURL=axis.d.ts.map
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dual-axis settings surface: the 0.1.7 spelling of the section 0.1.6
|
|
3
|
+
* registered through `ctx.settings.installSection`.
|
|
4
|
+
*
|
|
5
|
+
* 0.1.7 removed both `installSection` and `settings.register(ns, schema)`:
|
|
6
|
+
* `SettingsForms.describe()` projects exactly the Config schemas of LOADED
|
|
7
|
+
* Loader entries (`packages/settings/settings/src/index.ts:302-340`) and
|
|
8
|
+
* `write()` refuses a namespace with no matching entry
|
|
9
|
+
* (`:382-384`, `No configurable plugin entry`). The namespace IS the entry
|
|
10
|
+
* id, so this package's own row id in `cordis.patch.yml` — `dual-axis` — is
|
|
11
|
+
* the settings namespace. 0.1.6's separate `sandbox-axis` namespace name is
|
|
12
|
+
* therefore retired.
|
|
13
|
+
*
|
|
14
|
+
* 0.1.7 also added the volatile gate: a field that is not beneath a
|
|
15
|
+
* `.volatile()` node is neither projected into the form
|
|
16
|
+
* (`packages/settings/settings/src/schema.ts:37-47`) nor writable
|
|
17
|
+
* (`:74-78`), and an entry with no volatile field at all is refused outright
|
|
18
|
+
* (`index.ts:385-386`). Both axes are marked volatile here.
|
|
19
|
+
*
|
|
20
|
+
* The schema is deliberately `z.any()` per axis, exactly as in 0.1.6:
|
|
21
|
+
* schemastery's union types strip the `custom` branch's `base/allow/deny`
|
|
22
|
+
* payload, which would silently degrade a configured custom axis into an axis
|
|
23
|
+
* carrying no paths. Shape validation is {@link normalizeScope}'s job.
|
|
24
|
+
*
|
|
25
|
+
* @module @t4r71/dsh-dual-axis/config
|
|
26
|
+
*/
|
|
27
|
+
import type { Context, Volatile } from '@deepseek-ai/cordis';
|
|
28
|
+
import z from '@deepseek-ai/schemastery';
|
|
29
|
+
import type { AxisScope } from './axis.ts';
|
|
30
|
+
import type { RuleGroup } from './groups.ts';
|
|
31
|
+
/**
|
|
32
|
+
* This package's row id in `cordis.patch.yml`. It doubles as the 0.1.7
|
|
33
|
+
* settings namespace (the Loader entry id) and, prefixed with the package
|
|
34
|
+
* name, as the `plugins.row.config` slot entry key the client half uses.
|
|
35
|
+
*/
|
|
36
|
+
export declare const DUAL_AXIS_ROW_ID = "dual-axis";
|
|
37
|
+
/**
|
|
38
|
+
* The 0.1.6 settings namespace name, kept only so a migration can recognize
|
|
39
|
+
* the retired spelling. Nothing registers under it in 0.1.7.
|
|
40
|
+
*/
|
|
41
|
+
export declare const RETIRED_SETTINGS_NAMESPACE = "sandbox-axis";
|
|
42
|
+
/**
|
|
43
|
+
* This row's composition config: the defaults a NEWLY CREATED session takes,
|
|
44
|
+
* plus the group library its `defaultGroups` are drawn from.
|
|
45
|
+
*
|
|
46
|
+
* Every field here is a SEED. A session reads this row once, when it is created,
|
|
47
|
+
* and owns its copy from then on; the decision paths (the fence and the prompt)
|
|
48
|
+
* read the row only to resolve, by id, the definitions of the groups a session
|
|
49
|
+
* already references ({@link groupLibraryValue}). Nothing here is consulted when
|
|
50
|
+
* an existing session decides whether a path is allowed.
|
|
51
|
+
*
|
|
52
|
+
* Fields left empty fall back to the built-in defaults (read `all`, write
|
|
53
|
+
* `workspace`, no groups), which are 0.1.6's `DEFAULT_READ_SCOPE` /
|
|
54
|
+
* `DEFAULT_WRITE_SCOPE`.
|
|
55
|
+
*/
|
|
56
|
+
export interface Config {
|
|
57
|
+
/** The read axis a new session starts from (default: `{ kind: 'all' }`). */
|
|
58
|
+
read?: Volatile<AxisScope>;
|
|
59
|
+
/** The write axis a new session starts from (default: `{ kind: 'workspace' }`). */
|
|
60
|
+
write?: Volatile<AxisScope>;
|
|
61
|
+
/** The group library: named rule fragments sessions reference by id (default: empty). */
|
|
62
|
+
groups?: Volatile<RuleGroup[]>;
|
|
63
|
+
/** Which of {@link groups} a new session starts out referencing (default: empty). */
|
|
64
|
+
defaultGroups?: Volatile<string[]>;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The composition config's schema. Both axes are `.volatile()` and
|
|
68
|
+
* `z.any()` — see the module docstring for why each is required.
|
|
69
|
+
*
|
|
70
|
+
* Not annotated `z<Config>`: `.volatile()` widens the field's inferred type
|
|
71
|
+
* to schemastery's `Volatile<>` accessor, which the plain `Config` interface
|
|
72
|
+
* does not describe. The repo's own volatile Configs
|
|
73
|
+
* (`packages/core/agent-default-model/src/index.ts:51-55`) are inferred the
|
|
74
|
+
* same way, and {@link axesOf} re-establishes the declared shape.
|
|
75
|
+
*/
|
|
76
|
+
export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
77
|
+
read: z<any, any, "volatile">;
|
|
78
|
+
write: z<any, any, "volatile">;
|
|
79
|
+
groups: z<NoInfer<any[]>, NoInfer<any[]>, "volatile-defined">;
|
|
80
|
+
defaultGroups: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
81
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
82
|
+
read: z<any, any, "volatile">;
|
|
83
|
+
write: z<any, any, "volatile">;
|
|
84
|
+
groups: z<NoInfer<any[]>, NoInfer<any[]>, "volatile-defined">;
|
|
85
|
+
defaultGroups: z<NoInfer<string[]>, NoInfer<string[]>, "volatile-defined">;
|
|
86
|
+
}>>, "plain">;
|
|
87
|
+
/**
|
|
88
|
+
* Collect one `custom` axis's path list: every entry must be a non-empty
|
|
89
|
+
* absolute path.
|
|
90
|
+
* @param label - the axis name used in the error message.
|
|
91
|
+
* @param entry - the list name used in the error message (`allow` or `deny`).
|
|
92
|
+
* @param value - the untrusted list value.
|
|
93
|
+
* @returns the validated path list.
|
|
94
|
+
* @throws When the value is not a string array, or holds a non-absolute path.
|
|
95
|
+
*/
|
|
96
|
+
export declare function pathList(label: string, entry: string, value: unknown): readonly string[];
|
|
97
|
+
/**
|
|
98
|
+
* Coerce one axis value from a config document (which a human may have edited)
|
|
99
|
+
* into a legal {@link AxisScope}, throwing rather than guessing.
|
|
100
|
+
*
|
|
101
|
+
* An unreadable axis must not become some other, wider or narrower grant:
|
|
102
|
+
* throwing fails the row's mount or the settings write on the spot instead of
|
|
103
|
+
* proceeding under an invented boundary. This is 0.1.6's `normalizeScope`,
|
|
104
|
+
* unchanged.
|
|
105
|
+
* @param label - the axis name used in error messages (`read` or `write`).
|
|
106
|
+
* @param value - the untrusted axis value.
|
|
107
|
+
* @returns the validated axis value.
|
|
108
|
+
* @throws When the value is not one of the four kinds, or a custom entry is not absolute.
|
|
109
|
+
*/
|
|
110
|
+
export declare function normalizeScope(label: string, value: unknown): AxisScope;
|
|
111
|
+
/**
|
|
112
|
+
* Whether two axes describe the same boundary. Compared member by member
|
|
113
|
+
* rather than by reference: every re-parse builds fresh axis objects, and a
|
|
114
|
+
* custom axis's two path lists carry order as part of their value.
|
|
115
|
+
* @param left - one axis.
|
|
116
|
+
* @param right - another axis.
|
|
117
|
+
* @returns whether both describe the same axis.
|
|
118
|
+
*/
|
|
119
|
+
export declare function sameScope(left: AxisScope, right: AxisScope): boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Read both axes out of one settings/config value.
|
|
122
|
+
*
|
|
123
|
+
* The parameter is UNTRUSTED on purpose: the same function serves the
|
|
124
|
+
* composition layer's parsed Config, the settings service's projected
|
|
125
|
+
* descriptor (an `unknown` on the wire between two packages), and a
|
|
126
|
+
* hand-edited document, and none of those three is guaranteed to carry the
|
|
127
|
+
* declared shape. Both axes therefore pass through {@link normalizeScope},
|
|
128
|
+
* which is where an illegal value becomes a throw instead of an invented
|
|
129
|
+
* boundary; a missing side falls back to the built-in default axis.
|
|
130
|
+
* @param section - the section's current resolved value, whatever carries it.
|
|
131
|
+
* @returns both axes.
|
|
132
|
+
* @throws When either axis's shape is illegal.
|
|
133
|
+
*/
|
|
134
|
+
export declare function axesOf(section: unknown): {
|
|
135
|
+
read: AxisScope;
|
|
136
|
+
write: AxisScope;
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* This row's current value on the settings page, or `undefined` when no
|
|
140
|
+
* settings service is mounted.
|
|
141
|
+
*
|
|
142
|
+
* This is the ONE read path to that row. It goes through `describe()` rather
|
|
143
|
+
* than the config the plugin instance captured at mount because the loader does
|
|
144
|
+
* not commit a later save back to that instance
|
|
145
|
+
* (`vendor/loader/src/config/entry.ts:162-195`); `describe()` re-projects
|
|
146
|
+
* `entry.fiber.config` and unwraps the volatile accessors on every call
|
|
147
|
+
* (`packages/settings/settings/src/index.ts:319-323` +
|
|
148
|
+
* `packages/settings/settings/src/schema.ts:11`), which is the same path the
|
|
149
|
+
* settings page renders from. A save therefore applies without a restart.
|
|
150
|
+
* @param ctx - host context; the service is looked up, never injected.
|
|
151
|
+
* @returns the row's current value, or `undefined` when unavailable.
|
|
152
|
+
*/
|
|
153
|
+
export declare function declaredSection(ctx: Context): unknown;
|
|
154
|
+
/**
|
|
155
|
+
* The group library a decision path may read, and the ONLY part of the settings
|
|
156
|
+
* row one may read for a decision.
|
|
157
|
+
*
|
|
158
|
+
* The row's `read`, `write` and `defaultGroups` fields are seeds for
|
|
159
|
+
* NEWLY CREATED sessions and carry no authority over an existing one; a
|
|
160
|
+
* decision path that read them would let the settings page silently re-scope a
|
|
161
|
+
* running session, which is the drift this package exists to prevent.
|
|
162
|
+
* @param section - the row's current value, as {@link declaredSection} returns it.
|
|
163
|
+
* @returns the untrusted `groups` value, for {@link resolveEffectiveAxes}.
|
|
164
|
+
*/
|
|
165
|
+
export declare function groupLibraryValue(section: unknown): unknown;
|
|
166
|
+
/**
|
|
167
|
+
* The group ids a newly created session starts out referencing.
|
|
168
|
+
* @param section - the row's current value, as {@link declaredSection} returns it.
|
|
169
|
+
* @returns the ids, deduplicated; empty when the row declares none.
|
|
170
|
+
* @throws When a declared id is not a non-empty string.
|
|
171
|
+
*/
|
|
172
|
+
export declare function defaultGroupIds(section: unknown): readonly string[];
|
|
173
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 目标路径包含判定:把上游 `@deepseek-ai/dsh-fs-sandbox/containment` 的
|
|
3
|
+
* `isPathUnder` 原样内联到本包。
|
|
4
|
+
*
|
|
5
|
+
* 为什么必须内联:fs-sandbox@0.1.7-rc.2 的**发布包** exports 只开出
|
|
6
|
+
* `.`、`./src/*` 与 `./package.json` 三条,**没有 `./containment`**;
|
|
7
|
+
* 而源码工作区里该文件存在,靠路径解析能直接命中。于是同一个 import 在
|
|
8
|
+
* 工作区里编译通过、装成 tgz 后运行期必然抛
|
|
9
|
+
* `Package subpath './containment' is not defined by "exports"`,
|
|
10
|
+
* 使 dual-axis 这个 entry 永远无法激活(fiberPhase 恒为 null)。
|
|
11
|
+
* 自有副本消除这个 install-shape 依赖。
|
|
12
|
+
* @module @t4r71/dsh-dual-axis/containment
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Determine whether a canonical target is a writable root or lies beneath it.
|
|
16
|
+
* The lexical fast path handles normal canonical spellings. When spellings
|
|
17
|
+
* differ, walk the target's existing ancestors and compare filesystem identity
|
|
18
|
+
* with the root; this recognizes Windows long-name/8.3 aliases and casing
|
|
19
|
+
* without weakening containment to a textual approximation.
|
|
20
|
+
* @param path - canonical target key, which may end in a missing suffix.
|
|
21
|
+
* @param root - canonical writable root.
|
|
22
|
+
* @param caseSensitive - whether lexical comparison preserves case; defaults
|
|
23
|
+
* to the host filesystem convention used by supported platforms.
|
|
24
|
+
* @returns whether the target is the root or a descendant of it.
|
|
25
|
+
*/
|
|
26
|
+
export declare function isPathUnder(path: string, root: string, caseSensitive?: boolean): Promise<boolean>;
|
|
27
|
+
//# sourceMappingURL=containment.d.ts.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a session has been USED yet — the one predicate that decides when its
|
|
3
|
+
* axis pair freezes.
|
|
4
|
+
*
|
|
5
|
+
* A session the workspace picker reopens in the same workspace is the same
|
|
6
|
+
* session id with the same stored record, so the axes it was seeded with at
|
|
7
|
+
* creation are what its two dropdowns show for the rest of its life. That is
|
|
8
|
+
* correct for a session someone has actually worked in; for one nobody has ever
|
|
9
|
+
* exchanged a turn with it is wrong, because the settings row is a live default
|
|
10
|
+
* and the person changing it expects the next conversation to follow.
|
|
11
|
+
*
|
|
12
|
+
* So the record is written when the session is used, not when it is created:
|
|
13
|
+
*
|
|
14
|
+
* - nothing appended beyond the loop's runtime-context snapshot → no record; the
|
|
15
|
+
* seed recomputed from the CURRENT settings row answers every read, so the
|
|
16
|
+
* dropdown follows the settings page in real time;
|
|
17
|
+
* - one real user turn, or an assistant reply → the record is written and the
|
|
18
|
+
* pair freezes;
|
|
19
|
+
* - `/axis` → {@link SessionAxesStore.set} writes unconditionally, so a manual
|
|
20
|
+
* pick always freezes, on an empty session too.
|
|
21
|
+
*
|
|
22
|
+
* ## What counts as "content"
|
|
23
|
+
*
|
|
24
|
+
* Everything except a message whose source is the loop's own runtime-context
|
|
25
|
+
* snapshot. That snapshot is appended by the agent loop on EVERY turn
|
|
26
|
+
* (`packages/core/agent-loop/src/runtime-context.ts`), including the very first
|
|
27
|
+
* one, and it is present before any human input exists — the four events a
|
|
28
|
+
* freshly created session carries are the header, the preset, the mode, and the
|
|
29
|
+
* approval policy. Counting it would make every session look used the moment
|
|
30
|
+
* anything rendered its prompt, which is exactly the state this predicate has to
|
|
31
|
+
* tell apart.
|
|
32
|
+
*
|
|
33
|
+
* The snapshot is identified by its `source.kind`, the discriminant the loop
|
|
34
|
+
* writes and reads back (`isOwned` in that module requires `kind === 'plugin'`;
|
|
35
|
+
* the system-prompt renderer stamps `kind: 'runtime-context'`). Matching on the
|
|
36
|
+
* kind is cheaper and more robust than matching the rendered text, which is
|
|
37
|
+
* localized prose that changes with every contribution.
|
|
38
|
+
*
|
|
39
|
+
* Every other message counts, the skill catalogue and the compaction markers
|
|
40
|
+
* included: each of them means the session has entered a turn, and a session
|
|
41
|
+
* that has entered a turn is one somebody is using.
|
|
42
|
+
*
|
|
43
|
+
* @module @t4r71/dsh-dual-axis/content
|
|
44
|
+
*/
|
|
45
|
+
import type { Session } from '@deepseek-ai/dsh-session';
|
|
46
|
+
/**
|
|
47
|
+
* The event types that can carry content, so a listener can skip the rest without
|
|
48
|
+
* building the session's whole event list.
|
|
49
|
+
*
|
|
50
|
+
* A `system/message` is in here because it only exists once a turn has started,
|
|
51
|
+
* and a started turn is a used session. The prompt is assembled and that event
|
|
52
|
+
* appended BEFORE the human message reaches the log (measured: `system/message`
|
|
53
|
+
* at seq 7, the user turn at seq 8), so a rule that waited for the user message
|
|
54
|
+
* alone would let a tool-less turn finish without freezing anything.
|
|
55
|
+
*/
|
|
56
|
+
export declare const CONTENT_EVENT_TYPES: ReadonlySet<string>;
|
|
57
|
+
/**
|
|
58
|
+
* Whether a session has content beyond the loop's own runtime-context snapshots.
|
|
59
|
+
*
|
|
60
|
+
* Cheap by construction: it walks the session's existing event array, which is
|
|
61
|
+
* already materialized and cached, without copying, parsing, or allocating per
|
|
62
|
+
* event. It is called on every `ensure` for a session that has no record, and
|
|
63
|
+
* that set is exactly the sessions nobody has used — so the scan is short.
|
|
64
|
+
* @param session - the session to judge.
|
|
65
|
+
* @returns whether this session has been used.
|
|
66
|
+
*/
|
|
67
|
+
export declare function hasContent(session: Session): boolean;
|
|
68
|
+
//# sourceMappingURL=content.d.ts.map
|