@yanlinglabs/winter-agent-runtime 0.0.41 → 0.0.43
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/dist/commands/resolver.d.ts +8 -13
- package/dist/context/agent-listing.d.ts +7 -11
- package/dist/context/attachments.d.ts +14 -23
- package/dist/context/dynamic-sections.d.ts +5 -5
- package/dist/context/git-branch-fixture.d.ts +22 -0
- package/dist/context/git-status.d.ts +2 -2
- package/dist/context/memory.d.ts +1 -1
- package/dist/context/output-styles.d.ts +2 -2
- package/dist/context/request-layout.d.ts +16 -17
- package/dist/context/seam.d.ts +7 -6
- package/dist/embedded-worker.js +3 -3
- package/dist/embedded.js +3 -3
- package/dist/engine.d.ts +3 -3
- package/dist/{index-ey5c52j3.js → index-14xkt4jp.js} +2 -2
- package/dist/{index-hdvvezf8.js → index-bh1ve8ws.js} +238 -154
- package/dist/{index-z9yngxj4.js → index-kqc3tdph.js} +964 -826
- package/dist/index.js +1 -1
- package/dist/mcp/lifecycle.d.ts +2 -1
- package/dist/permissions/edit-recognition.d.ts +1 -1
- package/dist/permissions/evaluator.d.ts +19 -30
- package/dist/permissions/file-rules.d.ts +177 -277
- package/dist/permissions/grammar.d.ts +58 -0
- package/dist/permissions/policy-state.d.ts +1 -1
- package/dist/permissions/ruleset.d.ts +1 -1
- package/dist/permissions/shell-structure.d.ts +1 -1
- package/dist/plugins/bundle.d.ts +4 -6
- package/dist/plugins/loader.corpus-fixture.d.ts +26 -0
- package/dist/plugins/loader.d.ts +3 -4
- package/dist/plugins/manifest.d.ts +23 -46
- package/dist/production-wiring.d.ts +1 -1
- package/dist/provider/lean-prompt.d.ts +1 -6
- package/dist/sandbox/profile.d.ts +39 -79
- package/dist/sandbox/spawn.d.ts +5 -6
- package/dist/skills/listing.d.ts +8 -26
- package/dist/skills/store.d.ts +2 -3
- package/dist/store/dialect.d.ts +1 -1
- package/dist/subagents/availability.d.ts +10 -8
- package/dist/subagents/child-handle.d.ts +5 -5
- package/dist/subagents/definitions.d.ts +7 -6
- package/dist/subagents/fork.d.ts +4 -8
- package/dist/subagents/notification-queue.d.ts +30 -38
- package/dist/testing.js +2 -2
- package/dist/tools/descriptors/web-fetch.d.ts +1 -1
- package/dist/tools/descriptors/web-search.d.ts +1 -1
- package/dist/tools/impl/_search-budget.d.ts +1 -1
- package/dist/tools/impl/_web-fetch-net.d.ts +6 -6
- package/dist/tools/impl/_web-search-assembler.d.ts +7 -20
- package/dist/tools/impl/background-task-runtime.d.ts +13 -13
- package/dist/tools/impl/bash.d.ts +3 -3
- package/dist/tools/impl/monitor.d.ts +2 -2
- package/dist/toolsearch/exposure.d.ts +3 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/web/fetchable-url.d.ts +1 -1
- package/dist/web/preapproved-hosts.d.ts +9 -31
- package/dist/workflows/store.d.ts +23 -31
- package/package.json +4 -4
|
@@ -100,6 +100,12 @@ export declare function extractRedirectWrites(rawCommand: string): RedirectWrite
|
|
|
100
100
|
/** `extractRedirectWrites`, target paths only (quotes removed). */
|
|
101
101
|
export declare function extractRedirectTargets(command: string): string[];
|
|
102
102
|
export declare function isRecognizedReadOnly(command: string): boolean;
|
|
103
|
+
/**
|
|
104
|
+
* `s` with a backslash put before every character a JavaScript regular expression gives a meaning
|
|
105
|
+
* to -- `. * + ? ^ $ { } ( ) | [ ] \` -- so it can be embedded as literal text in a `RegExp` built at
|
|
106
|
+
* run time. Every other character is left as it is. Shared with `commands/resolver.ts`,
|
|
107
|
+
* `skills/permission-rules.ts` and `settings/env-filter.ts`.
|
|
108
|
+
*/
|
|
103
109
|
export declare function escapeRegExpLiteral(s: string): string;
|
|
104
110
|
/** The call's `url` as a parsed `URL`, or `undefined` when it is absent, not a string, or unparseable. NEVER throws. */
|
|
105
111
|
export declare function webFetchUrlOf(input: Record<string, unknown>): URL | undefined;
|
|
@@ -136,4 +142,56 @@ export interface PermissionRuleValidation {
|
|
|
136
142
|
error?: string;
|
|
137
143
|
suggestion?: string;
|
|
138
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Validates one raw rule string for `direction`. Every count and position below is taken on the RAW
|
|
147
|
+
* text as given, except where the split text is named. The checks, in order -- the first that fails
|
|
148
|
+
* decides:
|
|
149
|
+
*
|
|
150
|
+
* 1. Empty, or only whitespace: `{valid:false, error:"Permission rule cannot be empty"}`.
|
|
151
|
+
* 2. The number of unescaped `(` differs from the number of unescaped `)`:
|
|
152
|
+
* error `"Mismatched parentheses"`, suggestion `"Ensure all opening parentheses have matching
|
|
153
|
+
* closing parentheses"`.
|
|
154
|
+
* 3. Somewhere an unescaped `(` is immediately followed by `)`. Let `prefix` be the text before the
|
|
155
|
+
* FIRST `(` character of the raw rule, escaped or not. If `prefix` is empty: error
|
|
156
|
+
* `"Empty parentheses with no tool name"`, suggestion `"Specify a tool name before the
|
|
157
|
+
* parentheses"`; otherwise error `"Empty parentheses"`, suggestion
|
|
158
|
+
* `` `Either specify a pattern or use just "${prefix}" without parentheses` ``.
|
|
159
|
+
* 4. Split the trimmed text (`splitRuleText`). If it is not `Tool(content)`, the tool name is the
|
|
160
|
+
* whole trimmed text and there is no content; if the raw content is `""` or `"*"`, there is no
|
|
161
|
+
* content; otherwise the content is the unescaped raw content.
|
|
162
|
+
* 5. MCP names: split the tool name on `__`; when the first part is `mcp` and the second part is not
|
|
163
|
+
* empty, the second part is the server name and the rule is an MCP rule:
|
|
164
|
+
* - with content, or with any unescaped `(` in the raw text: error `"MCP rules do not support
|
|
165
|
+
* patterns in parentheses"`, suggestion
|
|
166
|
+
* `` `Use "${toolName}" without parentheses, or use "mcp__${server}__*" for all tools` ``;
|
|
167
|
+
* - for `allow`, a `*` in the server name is refused as in check 7 (a `*` only after a literal
|
|
168
|
+
* `mcp__<server>__` is fine);
|
|
169
|
+
* - otherwise valid. No further check applies to an MCP rule.
|
|
170
|
+
* 6. An empty tool name: error `"Tool name cannot be empty"`.
|
|
171
|
+
* 7. For `allow`, a tool name containing `*`: error
|
|
172
|
+
* `` `Wildcard tool name "${toolName}" is not supported in allow rules` ``, suggestion
|
|
173
|
+
* `"An allow pattern must name the scope it widens \u2014 globs are permitted only in the tool
|
|
174
|
+
* position after a literal mcp__<server>__ prefix. Deny and ask rules accept wildcards anywhere"`
|
|
175
|
+
* (with a real em dash, U+2014).
|
|
176
|
+
* 8. A tool name with no `_` whose first UTF-16 code unit differs from its upper-case form: error
|
|
177
|
+
* `"Tool names must start with uppercase"`, suggestion `` `Use "${first upper-cased}${rest}"` ``.
|
|
178
|
+
* 9. With content:
|
|
179
|
+
* - `WebSearch` content containing `*` or `?`: error `"WebSearch does not support wildcards"`,
|
|
180
|
+
* suggestion `"Use exact search terms without * or ?"`;
|
|
181
|
+
* - `WebFetch` content containing `://` or starting with `http`: error `"WebFetch permissions use
|
|
182
|
+
* domain format, not URLs"`, suggestion `'Use "domain:hostname" format'`; else content not
|
|
183
|
+
* starting with `domain:`: error `'WebFetch permissions must use "domain:" prefix'`, same
|
|
184
|
+
* suggestion.
|
|
185
|
+
* 10. `Bash` with content: content that contains `:*` but does not END with `:*` (an earlier `:*` is
|
|
186
|
+
* accepted when the content also ends with one, e.g. `a:*b:*`): error `"The :* pattern
|
|
187
|
+
* must be at the end"`, suggestion `"Move :* to the end for prefix matching, or use * for wildcard
|
|
188
|
+
* matching"`; content exactly `:*`: error `"Prefix cannot be empty before :*"`, suggestion
|
|
189
|
+
* `"Specify a command prefix before :*"`.
|
|
190
|
+
* 11. A file-pattern tool -- `Read`, `Write`, `Edit`, `Glob`, `NotebookRead`, `NotebookEdit`, `Cd`
|
|
191
|
+
* (this load-time list, not `FILE_RULE_TOOLS`: it has `NotebookRead` and `Cd` and not `Grep`) --
|
|
192
|
+
* with content containing `:*`: error `'The ":*" syntax is only for Bash prefix rules'`,
|
|
193
|
+
* suggestion `'Use glob patterns like "*" or "**" for file matching'`.
|
|
194
|
+
* 12. Otherwise `{valid:true}`.
|
|
195
|
+
* A result carries `suggestion` only where one is named above.
|
|
196
|
+
*/
|
|
139
197
|
export declare function validatePermissionRuleString(raw: string, direction: "allow" | "deny" | "ask"): PermissionRuleValidation;
|
|
@@ -14,7 +14,7 @@ export declare class WinterPermissionError extends Error {
|
|
|
14
14
|
export declare const PERMISSION_MODES: ReadonlySet<PermissionMode>;
|
|
15
15
|
export declare function isPermissionMode(value: string): value is PermissionMode;
|
|
16
16
|
/**
|
|
17
|
-
* SDK 0.0.16 (P16-7):
|
|
17
|
+
* SDK 0.0.16 (P16-7): Claude Code's fork agent carries `permissionMode: "bubble"` -- NOT a member
|
|
18
18
|
* of `PERMISSION_MODES` and never widened into one (`PermissionMode` is a closed 6-value union read
|
|
19
19
|
* exhaustively elsewhere -- `classifyPermissionMode` in particular). "bubble" is an explicit ALIAS
|
|
20
20
|
* for "no override": the child keeps whatever mode the parent session is CURRENTLY running (never a
|
|
@@ -6,7 +6,7 @@ export interface SourcedRuleEntry {
|
|
|
6
6
|
source: RuleSource;
|
|
7
7
|
ruleValue: PermissionRuleValue;
|
|
8
8
|
/**
|
|
9
|
-
* Fix round 11 ("important" item
|
|
9
|
+
* Fix round 11 ("important" item): the settings
|
|
10
10
|
* SOURCE's own directory, for a SINGLE-`/`-anchored pattern's root (`resolveFileRuleAnchor`'s own
|
|
11
11
|
* `/`-branch, file-rules.ts -- already fully wired to consume this, INERT_ANCHOR when absent, a
|
|
12
12
|
* pre-existing disclosed gap this field closes). Populated ONLY by `buildSettingsRuleSeed`
|
|
@@ -9,7 +9,7 @@ export interface HeredocExtraction {
|
|
|
9
9
|
* bodies bash will still expand. Bodies are removed BEFORE line continuations are joined: a QUOTED
|
|
10
10
|
* here-document's body is literal (`\<newline>` is not a continuation there), and joining first could
|
|
11
11
|
* shift its delimiter so that a later line -- `> /etc/passwd` -- was swallowed into the body and never
|
|
12
|
-
* seen
|
|
12
|
+
* seen.
|
|
13
13
|
*
|
|
14
14
|
* This is the one scanner that DROPS text, so it follows bash's contexts closely enough never to drop
|
|
15
15
|
* a command bash runs: `<<` is a here-document only in a command context (not inside `((…))`,
|
package/dist/plugins/bundle.d.ts
CHANGED
|
@@ -68,12 +68,10 @@ export interface PluginBundle {
|
|
|
68
68
|
* WS-21 fix round 4 (minors, M-3's last bullet): the manifest's own `workflows` key (`string |
|
|
69
69
|
* string[]`, `manifest.ts`'s own citation), resolved to absolute paths that exist and do not
|
|
70
70
|
* escape the plugin root -- present iff the manifest declares the key AND at least one entry
|
|
71
|
-
* resolved
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* is invalid ends up with NEITHER field set, exactly as claude leaves it with no workflows source
|
|
76
|
-
* at all rather than silently falling back to the shadowed default directory. Each entry may be a
|
|
71
|
+
* resolved. REPLACES `workflowsPath` rather than adding to it, and the replacement fires on the
|
|
72
|
+
* key's mere PRESENCE, not on whether anything resolved: a plugin whose every declared entry is
|
|
73
|
+
* invalid ends up with NEITHER field set, exactly as claude leaves it with no workflows source at
|
|
74
|
+
* all rather than silently falling back to the shadowed default directory. Each entry may be a
|
|
77
75
|
* directory or a single workflow file.
|
|
78
76
|
*/
|
|
79
77
|
workflowsPaths?: string[];
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { loadPlugins } from "./loader.js";
|
|
2
|
+
export interface PluginLayout {
|
|
3
|
+
dirs?: string[];
|
|
4
|
+
files?: Record<string, string>;
|
|
5
|
+
links?: Record<string, string>;
|
|
6
|
+
manifest?: unknown;
|
|
7
|
+
}
|
|
8
|
+
export type LoadPluginsFn = typeof loadPlugins;
|
|
9
|
+
/** Expands a content shorthand (see the header) into the file text it stands for; anything else is literal. */
|
|
10
|
+
export declare function expandContent(content: string): string;
|
|
11
|
+
export declare function materializeLayout(layout: PluginLayout): {
|
|
12
|
+
base: string;
|
|
13
|
+
root: string;
|
|
14
|
+
};
|
|
15
|
+
/** Runs `load` (defaults to the real `loadPlugins`) on a materialised layout; the result is JSON with `<tmp>` in place of the temp dir. */
|
|
16
|
+
export declare function runLayout(layout: PluginLayout, load?: LoadPluginsFn): unknown;
|
|
17
|
+
/**
|
|
18
|
+
* Whether a layout's result can depend on the file system folding case: some two distinct names it
|
|
19
|
+
* involves -- a path segment of a created directory, file or link, a link target, any string (or key)
|
|
20
|
+
* of the manifest, or a name the loader looks for -- differ only in case (`Skill.MD` beside the
|
|
21
|
+
* loader's `SKILL.md`, a manifest's `./Commands` beside `commands/`). On a case-insensitive volume
|
|
22
|
+
* (macOS, where the corpus was recorded) such names find each other; on a case-sensitive one they do
|
|
23
|
+
* not. Deliberately coarse: it compares single segments, so it may flag a layout whose answer would
|
|
24
|
+
* not in fact change, never miss one that would.
|
|
25
|
+
*/
|
|
26
|
+
export declare function dependsOnCaseFolding(layout: PluginLayout): boolean;
|
package/dist/plugins/loader.d.ts
CHANGED
|
@@ -23,9 +23,8 @@ export interface LoadPluginsResult {
|
|
|
23
23
|
agentFileRejections: AgentDefinitionRejection[];
|
|
24
24
|
/**
|
|
25
25
|
* Fix round 3 (M-5): a `hooks/hooks.json` that exists, parses as an object, but carries no
|
|
26
|
-
* top-level `"hooks"` key -- claude
|
|
27
|
-
* \`hooks\` (the hook matchers) or \`modules\` (hooks modules), or both
|
|
28
|
-
* plugin itself still loads (a malformed hooks file is not a whole-plugin rejection, matching
|
|
26
|
+
* top-level `"hooks"` key -- claude reports the same file as a hook-load failure (`hooks.json must
|
|
27
|
+
* have \`hooks\` (the hook matchers) or \`modules\` (hooks modules), or both`). The plugin itself still loads (a malformed hooks file is not a whole-plugin rejection, matching
|
|
29
28
|
* `agentFileRejections`'s own precedent immediately above), so this is the one channel that ever
|
|
30
29
|
* names it. `production-wiring.ts` folds these into the same `warnings` list.
|
|
31
30
|
*/
|
|
@@ -35,7 +34,7 @@ export interface LoadPluginsResult {
|
|
|
35
34
|
* `workflowsPathWarnings` -- ONE fold site, one channel, for every manifest custom-path override
|
|
36
35
|
* this loader resolves, not a parallel field per component): a manifest `workflows`/`agents`/
|
|
37
36
|
* `output-styles`/`commands`/`skills` entry that could not be used -- not a string, escapes the
|
|
38
|
-
* plugin directory (lexically OR through a symlink -- fix round 5's
|
|
37
|
+
* plugin directory (lexically OR through a symlink -- fix round 5's realpath fence), does not
|
|
39
38
|
* exist, or (skills only) is a file where a directory is required -- plus a
|
|
40
39
|
* `folder-shadowed-by-manifest` notice when an override silently drops an existing default
|
|
41
40
|
* directory. Named per-plugin, per-entry, on the SAME "recoverable, not a whole-plugin rejection"
|
|
@@ -27,70 +27,47 @@ export interface PluginManifest {
|
|
|
27
27
|
mcpServers?: Record<string, unknown>;
|
|
28
28
|
/**
|
|
29
29
|
* WS-21 fix round 4 (minors, M-3's last bullet): custom workflow source path(s), REPLACING the
|
|
30
|
-
* default `workflows/` directory rather than adding to it
|
|
31
|
-
* the
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* (qn.length>0)ve.workflowsPaths=qn}`, and the default directory is populated only through a
|
|
36
|
-
* SEPARATE `Lt=!j.workflows&&Be` gate a few lines above -- `Lt` is false whenever `j.workflows` is
|
|
37
|
-
* present at all, regardless of whether any entry resolves. Each entry may name a DIRECTORY
|
|
38
|
-
* (scanned the same way as the default one) or a single FILE (the SAME `Tb` call site passes
|
|
39
|
-
* `requireDirectory:!1` for `workflows`, unlike the `!0` it passes for `skills`, which is the
|
|
40
|
-
* fourth-from-last argument and the one place the two calls differ). `plugins/loader.ts`'s
|
|
41
|
-
* `resolveManifestComponentOverride` (fix round 5's own generalisation) is where this is resolved
|
|
42
|
-
* into `PluginBundle.workflowsPaths`.
|
|
30
|
+
* default `workflows/` directory rather than adding to it, as claude does: the default directory is
|
|
31
|
+
* not read whenever the key is present at all, regardless of whether any entry resolves. Each entry
|
|
32
|
+
* may name a DIRECTORY (scanned the same way as the default one) or a single FILE.
|
|
33
|
+
* `plugins/loader.ts`'s `resolveManifestComponentOverride` (fix round 5's own generalisation) is
|
|
34
|
+
* where this is resolved into `PluginBundle.workflowsPaths`.
|
|
43
35
|
*/
|
|
44
36
|
workflows?: string | string[];
|
|
45
37
|
/**
|
|
46
38
|
* WS-21 fix round 5: the same custom-path-override shape as `workflows` above, for the built-in
|
|
47
|
-
* `agents/` component --
|
|
48
|
-
* (
|
|
49
|
-
*
|
|
50
|
-
* (qn.length>0)ve.agentsPaths=qn}` -- `pt=!j.agents&&Fe` shadows the default directory on the
|
|
51
|
-
* key's mere presence, the identical `!j.X&&Y` shape `workflows`' own `Lt` gate has. The CONSUMER
|
|
52
|
-
* side (dump-confirmed separately, a different chunk of the same binary) stat()s each entry and
|
|
53
|
-
* branches directory-vs-file: `M.agentsPaths.map(async(he)=>{let ve=await stat(he);if(ve.
|
|
54
|
-
* isDirectory()){...scan the dir...}else if(...){...one file...}})` -- ported as
|
|
55
|
-
* `scanPluginAgentsOverride`.
|
|
39
|
+
* `agents/` component -- the key's mere presence shadows the default directory, and each entry is a
|
|
40
|
+
* directory (scanned like the default one) or a single agent file. `plugins/loader.ts`'s
|
|
41
|
+
* `scanPluginAgentsOverride` reads the entries.
|
|
56
42
|
*/
|
|
57
43
|
agents?: string | string[];
|
|
58
44
|
/**
|
|
59
45
|
* WS-21 fix round 5: the same custom-path-override shape as `workflows`/`agents`, for `commands`
|
|
60
|
-
* -- PLUS an inline `{<name>: {source?: string; content?: string}}` object-map form claude
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* miscast, and so the shadow-on-presence check still fires for it.
|
|
46
|
+
* -- PLUS an inline `{<name>: {source?: string; content?: string}}` object-map form claude also
|
|
47
|
+
* accepts, letting a manifest embed a command's text directly instead of pointing at a file. Winter
|
|
48
|
+
* does NOT support the inline form yet (`plugins/loader.ts`'s `resolveCommandsManifestOverride`
|
|
49
|
+
* warns about it); it is typed here only so a manifest using it is not silently miscast, and so the
|
|
50
|
+
* shadow-on-presence check still fires for it.
|
|
66
51
|
*/
|
|
67
52
|
commands?: string | string[] | Record<string, {
|
|
68
53
|
source?: string;
|
|
69
54
|
content?: string;
|
|
70
55
|
}>;
|
|
71
56
|
/**
|
|
72
|
-
* WS-21 fix round 5: `skills` is a manifest custom-path override too, but
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* skills
|
|
76
|
-
* `
|
|
77
|
-
* `j.skills` is also present; the CONSUMER (a separate chunk of the same binary) confirms it:
|
|
78
|
-
* `if(h.skillsPath){...load the default...}if(h.skillsPaths){...ALSO load every override entry...}`
|
|
79
|
-
* -- both run, never either/or. Consistent with this: `skills` is ABSENT from the
|
|
80
|
-
* `folder-shadowed-by-manifest` tuple list (`["commands",...],["agents",...],["outputStyles",...],
|
|
81
|
-
* ["themes",...]` -- no `"skills"` entry), so no shadow warning is possible for it.
|
|
82
|
-
* `plugins/loader.ts`'s `scanPluginSkillsOverride` is where this is resolved and merged.
|
|
57
|
+
* WS-21 fix round 5: `skills` is a manifest custom-path override too, but with a DIFFERENT
|
|
58
|
+
* precedence than every other component here -- ADDITIVE, not shadow-on-presence: claude loads the
|
|
59
|
+
* default `skills/` directory AND every override entry, never either/or, and never reports the
|
|
60
|
+
* default skills folder as shadowed. Each entry must be a directory (a parent of skill
|
|
61
|
+
* directories). `plugins/loader.ts`'s `resolvePluginSkills` is where this is resolved and merged.
|
|
83
62
|
*/
|
|
84
63
|
skills?: string | string[];
|
|
85
64
|
/**
|
|
86
65
|
* WS-21 fix round 5: the same custom-path-override shape as `agents`/`workflows` (shadow-on-
|
|
87
|
-
* presence,
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* scanned at load time): `context/output-styles.ts`'s `PluginOutputStyleSource.outputStylesPaths`
|
|
93
|
-
* is where a `<plugin>:<style>` lookup actually reads it.
|
|
66
|
+
* presence, a bare file allowed) for `output-styles` -- note the CAMEL-CASE manifest key, unlike
|
|
67
|
+
* the kebab-case default DIRECTORY name `output-styles/`. Resolved LAZILY (unlike
|
|
68
|
+
* commands/agents/skills, which are eagerly scanned at load time):
|
|
69
|
+
* `context/output-styles.ts`'s `PluginOutputStyleSource.outputStylesPaths` is where a
|
|
70
|
+
* `<plugin>:<style>` lookup actually reads it.
|
|
94
71
|
*/
|
|
95
72
|
outputStyles?: string | string[];
|
|
96
73
|
[key: string]: unknown;
|
|
@@ -275,7 +275,7 @@ export interface ProductionWiring {
|
|
|
275
275
|
* WS-21 §3.4.4 step 4 / §6.3 item 6: every settings tier's `env` block, claude's per-tier filters
|
|
276
276
|
* applied (`settings/env-filter.ts`), merged HIGHEST-PRECEDENCE-WINS. APPLIED by this function
|
|
277
277
|
* (fix round 1, item 5): `Object.assign(env, settingsEnv)` after every settings read, which reaches a
|
|
278
|
-
* tool spawn's process env
|
|
278
|
+
* tool spawn's process env, as claude applies each tier's filtered env to its own process env -- in
|
|
279
279
|
* production `env` IS `process.env`, and each test passes its own isolated object. Also exposed here
|
|
280
280
|
* so a caller (and this file's tests) can see exactly which variables were applied.
|
|
281
281
|
*/
|
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `modelKey` is a provider-qualified key (`anthropic/claude-opus-5`) or a bare id. claude compares a
|
|
3
|
-
* NORMALIZED id, so the Opus match tolerates the two spellings a catalog row really uses for the same
|
|
4
|
-
* build: a trailing `-YYYYMMDD` snapshot date, and a dotted minor (`claude-opus-4.1`). The original
|
|
5
|
-
* Opus 4 has no minor in its dated id (`claude-opus-4-20250514`); it is `claude-opus-4-0`.
|
|
6
|
-
*/
|
|
1
|
+
/** Whether a model key takes the full interface texts. */
|
|
7
2
|
export declare function claudeModelTakesFullPrompt(modelKey: string): boolean;
|
|
@@ -12,11 +12,11 @@ export interface SandboxFilesystemSettings {
|
|
|
12
12
|
allowRead?: string[];
|
|
13
13
|
denyRead?: string[];
|
|
14
14
|
/**
|
|
15
|
-
* Fix round 16, item 2
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
15
|
+
* Fix round 16, item 2: `sandbox.filesystem.allowGitConfig` in settings.json (default false) -- when
|
|
16
|
+
* true, a sandboxed command may write `.git/config`. The ONE settings-facing door for
|
|
17
|
+
* `SeatbeltProfileInput.allowGitConfigWrites`/`RunCommandOptions.allowGitConfigWrites`, which this
|
|
18
|
+
* module and spawn.ts already had (round 15) but nothing set. Wired through
|
|
19
|
+
* `tools/impl/{bash,monitor}.ts`'s own options-builders (each reads
|
|
20
20
|
* `ctx.sandboxSettings.filesystem?.allowGitConfig` directly) -- `buildSeatbeltProfile` itself never
|
|
21
21
|
* reads this field; it is CONSUMED at the caller boundary (spawn.ts's own `allowGitConfigWrites`
|
|
22
22
|
* param), matching every other filesystem key's own "settings shape carries it, a caller resolves
|
|
@@ -43,41 +43,6 @@ export declare class SandboxConfigError extends Error {
|
|
|
43
43
|
}
|
|
44
44
|
export declare function resolveNetworkPosture(network: SandboxNetworkSettings | undefined): boolean;
|
|
45
45
|
export declare function canonicalizePath(p: string): string;
|
|
46
|
-
/**
|
|
47
|
-
* Fix round 14 (Winter-specific hardening, NOT itself a claude port -- empirically discovered and
|
|
48
|
-
* verified while implementing `buildReadDenyWritePermitBlock` above, disclosed prominently rather
|
|
49
|
-
* than smoothed over): `pR`'s own trailing re-permit is a BLANKET, UNCONDITIONAL
|
|
50
|
-
* `(allow file-write-unlink file-write-create (subpath <every write root>))`, ported faithfully per
|
|
51
|
-
* the controller's own explicit instruction. Real `sandbox-exec` runs proved this is not merely
|
|
52
|
-
* "narrower than a later wildcard deny wins" (round 13's own finding, about an EARLIER explicit deny
|
|
53
|
-
* surviving a LATER broad `file-write*` allow) -- it runs the OTHER direction too: an EARLIER
|
|
54
|
-
* EXPLICIT `file-write-unlink`/`file-write-create` ALLOW is not overridden by a LATER, broader
|
|
55
|
-
* `(deny file-write* ...)` for the SAME target either. Seatbelt appears to give a clause naming
|
|
56
|
-
* `file-write-unlink`/`file-write-create` explicitly priority over one that only reaches those
|
|
57
|
-
* operations via the `file-write*` wildcard, independent of which clause is textually first or last.
|
|
58
|
-
*
|
|
59
|
-
* Every OTHER Winter-owned write-protection floor in this module that used only the `file-write*`
|
|
60
|
-
* wildcard was therefore silently punched through for CREATE and UNLINK/RENAME specifically (never
|
|
61
|
-
* for `file-write-data`, `file-write-mode`, etc., which this re-permit never names) by this ONE new
|
|
62
|
-
* block: the control-plane carve-outs (WS-12 §5.2's own "the seatbelt is the only enforcement point
|
|
63
|
-
* left" floor -- verified empirically: `mkdir -p .winter && echo '{}' > .winter/permissions.local.json`
|
|
64
|
-
* and `rm .winter/settings.json` both SUCCEEDED against the unpatched fix), the checkpoint/backup
|
|
65
|
-
* store write-deny (T8 rider 25's own identical floor), and a GLOB-shaped `denyWrite` entry (a plain
|
|
66
|
-
* `denyWritePaths` entry was already safe -- `Ch`'s own write-side ancestor-rename block, round 12,
|
|
67
|
-
* already emits an explicit `(subpath <path>)` deny for it; `Ch`'s own GLOB branch, by contrast, only
|
|
68
|
-
* ever emits a `(literal <fixedPrefix>)` -- protecting the prefix DIRECTORY's own identity against a
|
|
69
|
-
* rename-shuffle, never the glob-matched files themselves).
|
|
70
|
-
*
|
|
71
|
-
* The fix, verified against real `sandbox-exec` (a `(deny file-write* file-write-unlink
|
|
72
|
-
* file-write-create (regex ...))` clause DOES win back the CREATE it needs to, confirmed by a direct
|
|
73
|
-
* before/after run rather than assumed): every one of those floors now names
|
|
74
|
-
* `file-write-unlink`/`file-write-create` EXPLICITLY, alongside the `file-write*` wildcard it already
|
|
75
|
-
* carried (for the OTHER write operations the wildcard alone still covers correctly) -- this constant
|
|
76
|
-
* is that shared operation-name list, applied wherever `file-write*` ALONE previously appeared on a
|
|
77
|
-
* deny this round's own re-permit could otherwise reach. `fR` (`buildReadDenyKeepInPlaceBlock`) is
|
|
78
|
-
* deliberately NOT touched here: it already names `file-write-unlink` explicitly (never `create`, by
|
|
79
|
-
* claude's own design -- see that function's own header), so it was never in the affected set.
|
|
80
|
-
*/
|
|
81
46
|
/** The runtime's image working directory under the winter/store home (tools/image-prep.ts) -- write-denied to the shell. */
|
|
82
47
|
export declare const IMAGE_PREP_DIRNAME = "image-prep";
|
|
83
48
|
/**
|
|
@@ -94,7 +59,7 @@ export declare const IMAGE_PREP_DIRNAME = "image-prep";
|
|
|
94
59
|
* ESCAPE FIRST, FOLD SECOND, per character: a letter becomes `[Xx]`, and everything else is escaped
|
|
95
60
|
* exactly as `sbplRegexLiteral` escapes it (the brand grammar admits `-` and, for a dot-dir, the
|
|
96
61
|
* leading `.` -- which MUST be escaped or it matches any character). Applied to Winter's own dot-dir
|
|
97
|
-
* it renders `\.[Ww][Ii][Nn][Tt][Ee][Rr]`,
|
|
62
|
+
* it renders `\.[Ww][Ii][Nn][Tt][Ee][Rr]`, exactly what the constant it replaces spelled, so
|
|
98
63
|
* the rendered profile is unchanged under `WINTER_BRAND` (a test diffs the whole profile text).
|
|
99
64
|
*
|
|
100
65
|
* Exported so the deny suite can assert the rendering directly rather than by reading the profile.
|
|
@@ -110,46 +75,42 @@ export interface SeatbeltProfileInput {
|
|
|
110
75
|
/** WS-12 §5.3: filesystem.denyRead, layered AFTER the read-allow block (last-match-wins). */
|
|
111
76
|
denyReadPaths?: string[];
|
|
112
77
|
/**
|
|
113
|
-
* Fix round 11
|
|
114
|
-
*
|
|
115
|
-
* `
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
* (
|
|
121
|
-
*
|
|
122
|
-
* `tee`/`cp` bypassing that layer did not).
|
|
78
|
+
* Fix round 11: glob-shaped deny entries, PRE-CONVERTED by the caller to SBPL regex SOURCE TEXT
|
|
79
|
+
* (`permissions/file-rules.ts`'s `globToSbplRegexSource`/`recursiveGlobToSbplRegexSource`/
|
|
80
|
+
* `splitDenyPathsByGlobShape`) -- this module has no glob grammar of its own (mirrors
|
|
81
|
+
* `denyWritePaths`/`denyReadPaths`'s own "already resolved by the caller" posture) and only
|
|
82
|
+
* quotes/renders. A glob-shaped deny renders as `(regex ...)` and a plain one as `(subpath ...)`, as
|
|
83
|
+
* claude's macOS sandbox does; a `subpath` deny alone -- Winter's pre-round-11 posture -- silently
|
|
84
|
+
* dropped a glob-shaped Edit deny (e.g. a globstar-anchored `.env` pattern) or `denyWrite` entry from
|
|
85
|
+
* the sandbox layer entirely (the PERMISSION-RULE layer still enforced it for a recognized tool call;
|
|
86
|
+
* a bash-invoked `tee`/`cp` bypassing that layer did not).
|
|
123
87
|
*/
|
|
124
88
|
denyWriteRegexes?: string[];
|
|
125
89
|
denyReadRegexes?: string[];
|
|
126
90
|
/**
|
|
127
|
-
* Fix round 12
|
|
128
|
-
*
|
|
129
|
-
* `
|
|
130
|
-
*
|
|
131
|
-
* `
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
* ancestor of a denied path out of the way (and back) to slip a write past the deny.
|
|
91
|
+
* Fix round 12: the ancestor-rename fence. `denyWriteGlobFixedPrefixes`/`denyReadGlobFixedPrefixes`
|
|
92
|
+
* are the CANONICALIZED fixed-prefix directory of each glob-shaped denyWrite/denyRead entry
|
|
93
|
+
* (`permissions/file-rules.ts`'s `splitDenyPathsByGlobShape`, its own `globFixedPrefixes` output) --
|
|
94
|
+
* this module has no glob grammar of its own, mirrors the other caller-pre-resolved fields above.
|
|
95
|
+
* Combined with `denyWritePaths`/`denyReadPaths` (the PLAIN entries, reused directly),
|
|
96
|
+
* `buildAncestorRenameBypassBlock` builds a `(deny file-write-unlink file-write-create ...)` clause
|
|
97
|
+
* naming every ANCESTOR of each denied path/glob-fixed-prefix, PLUS the fixed prefix itself, so a
|
|
98
|
+
* sandboxed `mv <ancestor> <elsewhere> && <write inside where it used to be> && mv <elsewhere>
|
|
99
|
+
* <ancestor>` cannot rename an ancestor of a denied path out of the way (and back) to slip a write
|
|
100
|
+
* past the deny.
|
|
138
101
|
*/
|
|
139
102
|
denyWriteGlobFixedPrefixes?: string[];
|
|
140
103
|
denyReadGlobFixedPrefixes?: string[];
|
|
141
104
|
/**
|
|
142
|
-
* Fix round 13
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
* (`
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
* its own recursive clause (minus any write root nested INSIDE it, carved back out) and on every
|
|
152
|
-
* one of its ancestor directories that is ALSO inside a write root.
|
|
105
|
+
* Fix round 13: "keep read-denied paths inside write roots in place." The write-allow block
|
|
106
|
+
* (`(allow file-write* (subpath <root>))`) is emitted AFTER the read-deny section and, last match
|
|
107
|
+
* winning, overrode a read-deny's implicit protection against being UNLINKED (renamed away): with
|
|
108
|
+
* `Read(.env)` denied and cwd writable, a sandboxed `mv .env x && cat x` renamed the read-denied file
|
|
109
|
+
* to a new, non-denied name and read the secret through it. claude closes this too. A third section,
|
|
110
|
+
* emitted AFTER the write-allow block (`buildReadDenyKeepInPlaceBlock`), denies `file-write-unlink`
|
|
111
|
+
* for each read-denied path (or glob-shaped entry, this field) that sits inside a write root -- minus
|
|
112
|
+
* any write root nested INSIDE it, carved back out -- and for each of its ancestor directories that is
|
|
113
|
+
* ALSO inside a write root. Each entry pairs the glob's regex with its own fixed prefix.
|
|
153
114
|
*/
|
|
154
115
|
denyReadGlobEntries?: GlobDenyEntry[];
|
|
155
116
|
/** Resolved network posture -- see resolveNetworkPosture's own header for why this is a plain boolean here. */
|
|
@@ -210,12 +171,11 @@ export interface SeatbeltProfileInput {
|
|
|
210
171
|
*/
|
|
211
172
|
brand?: SandboxBrand;
|
|
212
173
|
/**
|
|
213
|
-
* Fix round 15
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
* true yet -- kept for parity since claude's own signature carries the knob.
|
|
174
|
+
* Fix round 15: when true, `.git/config` is NOT among the default write-protected entries
|
|
175
|
+
* (`buildDefaultWriteProtectionBlock`) -- every OTHER default protection (shell/tool config files,
|
|
176
|
+
* editor/agent dot-dirs, `.git/hooks`) is unaffected; this flag only ever gates `.git/config`.
|
|
177
|
+
* Omitted = `false` = protected, claude's default too. Set from `sandbox.filesystem.allowGitConfig`
|
|
178
|
+
* (round 16).
|
|
219
179
|
*/
|
|
220
180
|
allowGitConfigWrites?: boolean;
|
|
221
181
|
}
|
package/dist/sandbox/spawn.d.ts
CHANGED
|
@@ -84,7 +84,7 @@ export interface RunCommandOptions {
|
|
|
84
84
|
denyWritePaths?: string[];
|
|
85
85
|
denyReadPaths?: string[];
|
|
86
86
|
/**
|
|
87
|
-
* Fix round 11
|
|
87
|
+
* Fix round 11: glob-shaped `denyWritePaths`/
|
|
88
88
|
* `denyReadPaths` entries, PRE-CONVERTED to SBPL regex source by the caller (`permissions/
|
|
89
89
|
* file-rules.ts`'s `splitDenyPathsByGlobShape`) -- this module stays glob-grammar-free, exactly
|
|
90
90
|
* like `denyWritePaths`/`denyReadPaths` themselves are already resolved, absolute paths by the
|
|
@@ -93,7 +93,7 @@ export interface RunCommandOptions {
|
|
|
93
93
|
denyWriteRegexes?: string[];
|
|
94
94
|
denyReadRegexes?: string[];
|
|
95
95
|
/**
|
|
96
|
-
* Fix round 12
|
|
96
|
+
* Fix round 12: the ancestor-rename-bypass fix -- each
|
|
97
97
|
* glob-shaped `denyWritePaths`/`denyReadPaths` entry's OWN canonicalized fixed-prefix directory
|
|
98
98
|
* (see `SeatbeltProfileInput.denyWriteGlobFixedPrefixes`'s own header). Same "caller pre-resolves,
|
|
99
99
|
* this module stays glob-grammar-free" posture as `denyWriteRegexes`/`denyReadRegexes` above.
|
|
@@ -101,10 +101,9 @@ export interface RunCommandOptions {
|
|
|
101
101
|
denyWriteGlobFixedPrefixes?: string[];
|
|
102
102
|
denyReadGlobFixedPrefixes?: string[];
|
|
103
103
|
/**
|
|
104
|
-
* Fix round 13
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* read-deny-specific concern.
|
|
104
|
+
* Fix round 13: the read-deny-keep-in-place fix -- each glob-shaped `denyReadPaths` entry, PAIRED
|
|
105
|
+
* with its own fixed prefix (see `SeatbeltProfileInput.denyReadGlobEntries`'s own header).
|
|
106
|
+
* Read-side only -- keeping read-denied paths in place is a read-deny-specific concern.
|
|
108
107
|
*/
|
|
109
108
|
denyReadGlobEntries?: GlobDenyEntry[];
|
|
110
109
|
/**
|
package/dist/skills/listing.d.ts
CHANGED
|
@@ -41,49 +41,31 @@ export interface BuildSkillListingOptions {
|
|
|
41
41
|
export declare function isModelVisible(overrides: SkillOverrides | undefined, skill: SkillMeta | string): boolean;
|
|
42
42
|
/** True when a USER `/name` may still reach this skill. Only `off` closes that door. */
|
|
43
43
|
export declare function isUserInvocable(overrides: SkillOverrides | undefined, skill: SkillMeta | string): boolean;
|
|
44
|
-
/**
|
|
45
|
-
* claude's `Ckn`: the context window a budget is sized against when the session reports none.
|
|
46
|
-
*/
|
|
44
|
+
/** The context window (tokens) a budget is sized against when the session reports none. */
|
|
47
45
|
export declare const DEFAULT_SKILL_LISTING_CONTEXT_WINDOW_TOKENS = 200000;
|
|
48
|
-
/**
|
|
49
|
-
* The whole-listing character budget -- claude's `Ige`: `floor(window x chars-per-token x fraction)`,
|
|
50
|
-
* with claude's 200k-token default window when the session reports none (or a non-finite one).
|
|
51
|
-
* An explicit `budgetChars` wins (claude's `SLASH_COMMAND_TOOL_CHAR_BUDGET` analog), and there `0`
|
|
52
|
-
* means "no budget" -- a Winter extension, never "no listing".
|
|
53
|
-
*/
|
|
46
|
+
/** The whole-listing character budget: an explicit `budgetChars` (`0` = no budget), or a share of the context window in characters. */
|
|
54
47
|
export declare function skillListingBudgetChars(opts: {
|
|
55
48
|
contextWindowTokens?: number | undefined;
|
|
56
49
|
budgetFraction?: number | undefined;
|
|
57
50
|
budgetChars?: number | undefined;
|
|
58
51
|
}): number;
|
|
59
52
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
53
|
+
* A description longer than the cap keeps `cap - 1` characters and gains the ellipsis, so the result
|
|
54
|
+
* is exactly `cap` characters long (the same shape claude's listing uses).
|
|
62
55
|
*/
|
|
63
56
|
export declare function truncateSkillDescription(description: string, maxDescChars: number): string;
|
|
64
57
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
58
|
+
* One listing line: `- <name>: <description>`, or `- <name>` for an entry whose description is
|
|
59
|
+
* empty (a `name-only` override, or one the budget reduced to its name).
|
|
67
60
|
*/
|
|
68
61
|
export declare function renderSkillListingLine(entry: {
|
|
69
62
|
name: string;
|
|
70
63
|
description: string;
|
|
71
64
|
}): string;
|
|
72
|
-
/** The `skill_listing` attachment's `content`: one line per entry, newline-joined
|
|
65
|
+
/** The `skill_listing` attachment's `content`: one line per entry, newline-joined. */
|
|
73
66
|
export declare function renderSkillListingContent(entries: readonly {
|
|
74
67
|
name: string;
|
|
75
68
|
description: string;
|
|
76
69
|
}[]): string;
|
|
77
|
-
/**
|
|
78
|
-
* Build the model-facing listing -- claude 0.3.250's `Rot`, applied here once so every consumer sees
|
|
79
|
-
* the budgeted result.
|
|
80
|
-
*
|
|
81
|
-
* ORDER IS PRECEDENCE ORDER (the index's own): project first, builtin last. Every visible skill is
|
|
82
|
-
* listed; the budget decides only which ones keep their DESCRIPTION:
|
|
83
|
-
* - everything fits -> every line is full;
|
|
84
|
-
* - over budget -> `name-only` entries and Winter's builtin (claude: bundled) skills stay full,
|
|
85
|
-
* every other entry starts as its bare name, and descriptions are restored in priority order
|
|
86
|
-
* while the remaining budget holds them. claude orders that priority by its per-skill usage
|
|
87
|
-
* score; Winter keeps no usage history, so every score ties and precedence order decides.
|
|
88
|
-
*/
|
|
70
|
+
/** The model-facing listing: every visible skill, with the budget deciding which keep their description. */
|
|
89
71
|
export declare function buildSkillListing(skills: readonly SkillMeta[], opts?: BuildSkillListingOptions): SkillListing;
|
package/dist/skills/store.d.ts
CHANGED
|
@@ -88,9 +88,8 @@ export interface SkillIndexOptions {
|
|
|
88
88
|
bodyBytes?: number | undefined;
|
|
89
89
|
/**
|
|
90
90
|
* Fix round 4 (I-E, the router same-view test): a workflow, registered as a SYNTHETIC skill so
|
|
91
|
-
* `Skill("<name>")` and the slash-command surface both run it -- claude
|
|
92
|
-
* discovered workflow
|
|
93
|
-
* ...}`). Unlike every other entry `discover()` finds, a synthetic entry carries its own BODY
|
|
91
|
+
* `Skill("<name>")` and the slash-command surface both run it -- claude likewise offers every
|
|
92
|
+
* discovered workflow as a prompt-type command under the workflow's name. Unlike every other entry `discover()` finds, a synthetic entry carries its own BODY
|
|
94
93
|
* directly (`load()` returns it as-is, never reading `path` from disk -- there is no SKILL.md a
|
|
95
94
|
* workflow script's own identity could point `load()` at). `path` is still required on the
|
|
96
95
|
* resulting `SkillMeta` (a synthetic, non-filesystem marker -- `workflows/store.ts`'s own
|
package/dist/store/dialect.d.ts
CHANGED
|
@@ -478,7 +478,7 @@ export declare class TranscriptWriter implements SessionPersistence {
|
|
|
478
478
|
private dialectRecord;
|
|
479
479
|
static readBack(store: SessionStore, key: SessionKey): Promise<SessionStoreEntry[]>;
|
|
480
480
|
}
|
|
481
|
-
export declare const RUNTIME_ENGINE_VERSION = "0.0.
|
|
481
|
+
export declare const RUNTIME_ENGINE_VERSION = "0.0.43";
|
|
482
482
|
export interface ResolvedEngineSession {
|
|
483
483
|
config: RuntimeConfig;
|
|
484
484
|
store: SessionPersistence | undefined;
|