@yanlinglabs/winter-agent-runtime 0.0.27
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/NOTICE +41 -0
- package/README.md +64 -0
- package/dist/checkpoint/file-history.d.ts +81 -0
- package/dist/checkpoint/rewind.d.ts +55 -0
- package/dist/checkpoint/seam.d.ts +47 -0
- package/dist/checkpoint/sink.d.ts +66 -0
- package/dist/commands/builtins-listing.d.ts +40 -0
- package/dist/commands/resolver.d.ts +103 -0
- package/dist/commands/seam.d.ts +53 -0
- package/dist/compaction/controller.d.ts +23 -0
- package/dist/compaction/retention.d.ts +35 -0
- package/dist/compaction/seam.d.ts +115 -0
- package/dist/compaction/summarizer.d.ts +79 -0
- package/dist/context/agent-listing.d.ts +39 -0
- package/dist/context/assembler.d.ts +46 -0
- package/dist/context/attachments.d.ts +104 -0
- package/dist/context/dynamic-sections.d.ts +31 -0
- package/dist/context/git-fixture.d.ts +18 -0
- package/dist/context/git-status.d.ts +16 -0
- package/dist/context/imports.d.ts +22 -0
- package/dist/context/injection.d.ts +53 -0
- package/dist/context/memory-key.d.ts +46 -0
- package/dist/context/memory.d.ts +28 -0
- package/dist/context/minimal-prompt.d.ts +5 -0
- package/dist/context/output-styles.d.ts +68 -0
- package/dist/context/plan-mode.d.ts +29 -0
- package/dist/context/request-layout.d.ts +138 -0
- package/dist/context/rules.d.ts +63 -0
- package/dist/context/seam.d.ts +136 -0
- package/dist/context/tool-epoch.d.ts +118 -0
- package/dist/context/winter-code-preset.d.ts +39 -0
- package/dist/context/winter-md.d.ts +81 -0
- package/dist/embedded-host.d.ts +48 -0
- package/dist/embedded-host.js +155 -0
- package/dist/embedded-protocol.d.ts +44 -0
- package/dist/embedded-worker.d.ts +1 -0
- package/dist/embedded-worker.js +74 -0
- package/dist/embedded.d.ts +34 -0
- package/dist/embedded.js +9 -0
- package/dist/engine.d.ts +1298 -0
- package/dist/hooks/additional-context.d.ts +21 -0
- package/dist/hooks/bounds.d.ts +6 -0
- package/dist/hooks/bridge-invoker.d.ts +3 -0
- package/dist/hooks/command-invoker.d.ts +52 -0
- package/dist/hooks/from-config.d.ts +31 -0
- package/dist/hooks/hook-stage.d.ts +25 -0
- package/dist/hooks/input-validator.d.ts +6 -0
- package/dist/hooks/reducer.d.ts +74 -0
- package/dist/hooks/registry.d.ts +33 -0
- package/dist/hooks/runner.d.ts +105 -0
- package/dist/index-584yahed.js +6037 -0
- package/dist/index-97t2rmtf.js +42 -0
- package/dist/index-9qgkpv56.js +27183 -0
- package/dist/index-bef62z3r.js +437 -0
- package/dist/index-rkhh0457.js +187 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +353 -0
- package/dist/main.d.ts +1 -0
- package/dist/mcp/client.d.ts +105 -0
- package/dist/mcp/control-seam.d.ts +25 -0
- package/dist/mcp/control.d.ts +5 -0
- package/dist/mcp/elicitation.d.ts +35 -0
- package/dist/mcp/env.d.ts +12 -0
- package/dist/mcp/lifecycle.d.ts +142 -0
- package/dist/mcp/output-cap.d.ts +15 -0
- package/dist/mcp/state.d.ts +24 -0
- package/dist/mcp/test-fixtures.d.ts +88 -0
- package/dist/mcp/transports/__fixtures__/stdio-server.d.ts +1 -0
- package/dist/mcp/transports/http.d.ts +5 -0
- package/dist/mcp/transports/sdk.d.ts +5 -0
- package/dist/mcp/transports/sse.d.ts +3 -0
- package/dist/mcp/transports/stdio.d.ts +35 -0
- package/dist/mcp/winter-server.d.ts +2 -0
- package/dist/messaging/reference-adapter.d.ts +88 -0
- package/dist/messaging/router.d.ts +8 -0
- package/dist/paths/project-dir-name.d.ts +2 -0
- package/dist/paths/temp.d.ts +25 -0
- package/dist/permissions/approvals.d.ts +107 -0
- package/dist/permissions/auto/caches.d.ts +53 -0
- package/dist/permissions/auto/config.d.ts +37 -0
- package/dist/permissions/auto/engine.d.ts +74 -0
- package/dist/permissions/auto/envelope.d.ts +45 -0
- package/dist/permissions/auto/inheritance.d.ts +27 -0
- package/dist/permissions/edit-recognition.d.ts +30 -0
- package/dist/permissions/evaluator.d.ts +284 -0
- package/dist/permissions/file-rules.d.ts +384 -0
- package/dist/permissions/grammar.d.ts +113 -0
- package/dist/permissions/paths.d.ts +32 -0
- package/dist/permissions/policy-state.d.ts +64 -0
- package/dist/permissions/prompt-stage.d.ts +3 -0
- package/dist/permissions/protected.d.ts +54 -0
- package/dist/permissions/ruleset.d.ts +134 -0
- package/dist/permissions/shell-structure.d.ts +41 -0
- package/dist/plugins/bundle.d.ts +100 -0
- package/dist/plugins/installed.d.ts +30 -0
- package/dist/plugins/loader.d.ts +56 -0
- package/dist/plugins/manifest.d.ts +115 -0
- package/dist/production-wiring.d.ts +340 -0
- package/dist/protocol/channel.d.ts +22 -0
- package/dist/provider/advisor-route.d.ts +47 -0
- package/dist/provider/bridge.d.ts +124 -0
- package/dist/provider/classifier/model-classifier.d.ts +82 -0
- package/dist/provider/classifier/prompt.d.ts +62 -0
- package/dist/provider/classifier/verdict-schema.d.ts +83 -0
- package/dist/provider/credential-api.d.ts +160 -0
- package/dist/provider/family-listing.d.ts +27 -0
- package/dist/provider/first-party.d.ts +4 -0
- package/dist/provider/keychain-store.d.ts +59 -0
- package/dist/provider/lean-prompt.d.ts +7 -0
- package/dist/provider/mock.d.ts +55 -0
- package/dist/provider/scenario-fake.d.ts +96 -0
- package/dist/provider/selection.d.ts +115 -0
- package/dist/provider/session-provider.d.ts +426 -0
- package/dist/provider/slots.d.ts +120 -0
- package/dist/provider/stream-frames.d.ts +25 -0
- package/dist/provider/tool-secret.d.ts +57 -0
- package/dist/rpc/bridge.d.ts +14 -0
- package/dist/rpc/mcp-control.d.ts +26 -0
- package/dist/runtime.d.ts +14 -0
- package/dist/sandbox/profile.d.ts +249 -0
- package/dist/sandbox/spawn.d.ts +139 -0
- package/dist/settings/env-filter.d.ts +52 -0
- package/dist/settings/loaders/hooks.d.ts +44 -0
- package/dist/settings/loaders/mcp-config.d.ts +83 -0
- package/dist/settings/loaders/plugin-mcp.d.ts +3 -0
- package/dist/settings/loaders/strict-plugin-only.d.ts +13 -0
- package/dist/settings/resolve.d.ts +2 -0
- package/dist/settings/sources.d.ts +2 -0
- package/dist/settings/trust.d.ts +36 -0
- package/dist/skills/attachment.d.ts +25 -0
- package/dist/skills/frontmatter.d.ts +64 -0
- package/dist/skills/index.d.ts +16 -0
- package/dist/skills/listing.d.ts +89 -0
- package/dist/skills/loader.d.ts +104 -0
- package/dist/skills/option.d.ts +68 -0
- package/dist/skills/permission-rules.d.ts +21 -0
- package/dist/skills/runtime.d.ts +21 -0
- package/dist/skills/store.d.ts +163 -0
- package/dist/store/continuation-attach.d.ts +44 -0
- package/dist/store/dialect.d.ts +526 -0
- package/dist/store/provider-state.d.ts +188 -0
- package/dist/store/resume.d.ts +92 -0
- package/dist/structured/ajv-seam.d.ts +7 -0
- package/dist/structured/descriptor.d.ts +9 -0
- package/dist/structured/seam.d.ts +51 -0
- package/dist/structured/validator.d.ts +22 -0
- package/dist/subagents/activity.d.ts +13 -0
- package/dist/subagents/availability.d.ts +30 -0
- package/dist/subagents/builtin-agents.d.ts +37 -0
- package/dist/subagents/child-engine.d.ts +198 -0
- package/dist/subagents/child-handle.d.ts +344 -0
- package/dist/subagents/definitions.d.ts +189 -0
- package/dist/subagents/fork.d.ts +55 -0
- package/dist/subagents/git-root.d.ts +1 -0
- package/dist/subagents/limits.d.ts +28 -0
- package/dist/subagents/notification-queue.d.ts +233 -0
- package/dist/subagents/plugin-agents.d.ts +5 -0
- package/dist/subagents/policy.d.ts +46 -0
- package/dist/subagents/register-default-factory.d.ts +66 -0
- package/dist/subagents/resolution.d.ts +56 -0
- package/dist/subagents/restore.d.ts +9 -0
- package/dist/subagents/roster.d.ts +17 -0
- package/dist/subagents/test-fakes.d.ts +12 -0
- package/dist/subagents/tool-pools.d.ts +69 -0
- package/dist/subagents/watchdog.d.ts +13 -0
- package/dist/subagents/workspace.d.ts +27 -0
- package/dist/testing.d.ts +5 -0
- package/dist/testing.js +194 -0
- package/dist/tools/background-tasks.d.ts +10 -0
- package/dist/tools/descriptors/_shared.d.ts +34 -0
- package/dist/tools/descriptors/advisor.d.ts +1 -0
- package/dist/tools/descriptors/agent.d.ts +47 -0
- package/dist/tools/descriptors/artifact.d.ts +1 -0
- package/dist/tools/descriptors/ask-user-question.d.ts +1 -0
- package/dist/tools/descriptors/bash.d.ts +20 -0
- package/dist/tools/descriptors/claude-design.d.ts +1 -0
- package/dist/tools/descriptors/cron-create.d.ts +1 -0
- package/dist/tools/descriptors/cron-delete.d.ts +1 -0
- package/dist/tools/descriptors/cron-list.d.ts +1 -0
- package/dist/tools/descriptors/edit.d.ts +1 -0
- package/dist/tools/descriptors/end-conversation.d.ts +1 -0
- package/dist/tools/descriptors/enter-plan-mode.d.ts +1 -0
- package/dist/tools/descriptors/enter-worktree.d.ts +1 -0
- package/dist/tools/descriptors/exit-plan-mode.d.ts +1 -0
- package/dist/tools/descriptors/exit-worktree.d.ts +1 -0
- package/dist/tools/descriptors/glob.d.ts +1 -0
- package/dist/tools/descriptors/grep.d.ts +1 -0
- package/dist/tools/descriptors/index.d.ts +59 -0
- package/dist/tools/descriptors/list-agents.d.ts +1 -0
- package/dist/tools/descriptors/list-mcp-resources-tool.d.ts +1 -0
- package/dist/tools/descriptors/lsp.d.ts +1 -0
- package/dist/tools/descriptors/monitor.d.ts +1 -0
- package/dist/tools/descriptors/notebook-edit.d.ts +1 -0
- package/dist/tools/descriptors/powershell.d.ts +1 -0
- package/dist/tools/descriptors/projects.d.ts +1 -0
- package/dist/tools/descriptors/propose-goal.d.ts +1 -0
- package/dist/tools/descriptors/propose-skills.d.ts +1 -0
- package/dist/tools/descriptors/push-notification.d.ts +1 -0
- package/dist/tools/descriptors/read-mcp-resource-dir-tool.d.ts +1 -0
- package/dist/tools/descriptors/read-mcp-resource-tool.d.ts +1 -0
- package/dist/tools/descriptors/read-notifications.d.ts +1 -0
- package/dist/tools/descriptors/read.d.ts +1 -0
- package/dist/tools/descriptors/refresh-mcp-tools.d.ts +1 -0
- package/dist/tools/descriptors/remote-trigger.d.ts +1 -0
- package/dist/tools/descriptors/repl.d.ts +1 -0
- package/dist/tools/descriptors/report-findings.d.ts +1 -0
- package/dist/tools/descriptors/schedule-wakeup.d.ts +1 -0
- package/dist/tools/descriptors/send-feedback.d.ts +1 -0
- package/dist/tools/descriptors/send-message.d.ts +1 -0
- package/dist/tools/descriptors/send-user-file.d.ts +1 -0
- package/dist/tools/descriptors/share-onboarding-guide.d.ts +1 -0
- package/dist/tools/descriptors/show-onboarding-role-picker.d.ts +1 -0
- package/dist/tools/descriptors/skill.d.ts +1 -0
- package/dist/tools/descriptors/structured-output.d.ts +1 -0
- package/dist/tools/descriptors/task-create.d.ts +1 -0
- package/dist/tools/descriptors/task-get.d.ts +1 -0
- package/dist/tools/descriptors/task-list.d.ts +1 -0
- package/dist/tools/descriptors/task-output.d.ts +1 -0
- package/dist/tools/descriptors/task-stop.d.ts +1 -0
- package/dist/tools/descriptors/task-update.d.ts +1 -0
- package/dist/tools/descriptors/todo-write.d.ts +1 -0
- package/dist/tools/descriptors/tool-search.d.ts +1 -0
- package/dist/tools/descriptors/wait-for-mcp-servers.d.ts +1 -0
- package/dist/tools/descriptors/web-fetch.d.ts +8 -0
- package/dist/tools/descriptors/web-search.d.ts +15 -0
- package/dist/tools/descriptors/winter-list-agents.d.ts +1 -0
- package/dist/tools/descriptors/winter-send-message.d.ts +1 -0
- package/dist/tools/descriptors/workflow.d.ts +1 -0
- package/dist/tools/descriptors/write.d.ts +1 -0
- package/dist/tools/impl/_caller.d.ts +14 -0
- package/dist/tools/impl/_domains.d.ts +25 -0
- package/dist/tools/impl/_exa-client.d.ts +122 -0
- package/dist/tools/impl/_exa-session-client.d.ts +23 -0
- package/dist/tools/impl/_inner-model.d.ts +135 -0
- package/dist/tools/impl/_search-budget.d.ts +36 -0
- package/dist/tools/impl/_web-fetch-cache.d.ts +37 -0
- package/dist/tools/impl/_web-fetch-html.d.ts +26 -0
- package/dist/tools/impl/_web-fetch-net.d.ts +99 -0
- package/dist/tools/impl/_web-search-assembler.d.ts +57 -0
- package/dist/tools/impl/advisor.d.ts +37 -0
- package/dist/tools/impl/agent.d.ts +10 -0
- package/dist/tools/impl/ask-user-question.d.ts +5 -0
- package/dist/tools/impl/background-task-runtime.d.ts +272 -0
- package/dist/tools/impl/bash.d.ts +78 -0
- package/dist/tools/impl/cron.d.ts +11 -0
- package/dist/tools/impl/edit.d.ts +1 -0
- package/dist/tools/impl/enter-plan-mode.d.ts +4 -0
- package/dist/tools/impl/enter-worktree.d.ts +25 -0
- package/dist/tools/impl/exit-plan-mode.d.ts +4 -0
- package/dist/tools/impl/exit-worktree.d.ts +4 -0
- package/dist/tools/impl/glob.d.ts +1 -0
- package/dist/tools/impl/grep.d.ts +19 -0
- package/dist/tools/impl/index.d.ts +37 -0
- package/dist/tools/impl/list-agents.d.ts +7 -0
- package/dist/tools/impl/list-mcp-resources-tool.d.ts +9 -0
- package/dist/tools/impl/monitor.d.ts +66 -0
- package/dist/tools/impl/notebook-edit.d.ts +1 -0
- package/dist/tools/impl/push-notification.d.ts +4 -0
- package/dist/tools/impl/read-ladder.d.ts +25 -0
- package/dist/tools/impl/read-mcp-resource-dir-tool.d.ts +9 -0
- package/dist/tools/impl/read-mcp-resource-tool.d.ts +9 -0
- package/dist/tools/impl/read-notifications.d.ts +5 -0
- package/dist/tools/impl/read.d.ts +44 -0
- package/dist/tools/impl/refresh-mcp-tools.d.ts +8 -0
- package/dist/tools/impl/report-findings.d.ts +1 -0
- package/dist/tools/impl/schedule-wakeup.d.ts +2 -0
- package/dist/tools/impl/send-message.d.ts +7 -0
- package/dist/tools/impl/skill.d.ts +5 -0
- package/dist/tools/impl/task-graph.d.ts +1 -0
- package/dist/tools/impl/task-output.d.ts +16 -0
- package/dist/tools/impl/task-stop.d.ts +11 -0
- package/dist/tools/impl/todo-write.d.ts +8 -0
- package/dist/tools/impl/tool-search.d.ts +4 -0
- package/dist/tools/impl/wait-for-mcp-servers.d.ts +29 -0
- package/dist/tools/impl/web-fetch.d.ts +36 -0
- package/dist/tools/impl/web-search.d.ts +30 -0
- package/dist/tools/impl/workflow.d.ts +5 -0
- package/dist/tools/impl/write.d.ts +8 -0
- package/dist/tools/paths-seam.d.ts +6 -0
- package/dist/tools/read-state.d.ts +12 -0
- package/dist/tools/registry.d.ts +423 -0
- package/dist/tools/task-graph-store.d.ts +61 -0
- package/dist/toolsearch/aliases.d.ts +26 -0
- package/dist/toolsearch/exposure.d.ts +30 -0
- package/dist/toolsearch/ranking.d.ts +6 -0
- package/dist/toolsearch/search.d.ts +45 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +5 -0
- package/dist/web/fetchable-url.d.ts +28 -0
- package/dist/web/preapproved-hosts.d.ts +52 -0
- package/dist/web/private-address.d.ts +55 -0
- package/dist/web/session-runtime.d.ts +86 -0
- package/dist/workflows/bridge.d.ts +87 -0
- package/dist/workflows/budget.d.ts +24 -0
- package/dist/workflows/host-registry.d.ts +142 -0
- package/dist/workflows/journal.d.ts +29 -0
- package/dist/workflows/meta.d.ts +45 -0
- package/dist/workflows/registry.d.ts +24 -0
- package/dist/workflows/runtime.d.ts +254 -0
- package/dist/workflows/sandbox.d.ts +49 -0
- package/dist/workflows/script-api.d.ts +58 -0
- package/dist/workflows/seam.d.ts +92 -0
- package/dist/workflows/semaphore.d.ts +16 -0
- package/dist/workflows/store.d.ts +177 -0
- package/dist/workflows/subprocess-entry.d.ts +38 -0
- package/dist/workflows/subprocess-entry.js +14 -0
- package/dist/workflows/transcript.d.ts +56 -0
- package/dist/workflows/types.d.ts +84 -0
- package/dist/workflows/worker-harness.d.ts +45 -0
- package/package.json +76 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
export type FileRuleKind = "edit" | "read";
|
|
2
|
+
/**
|
|
3
|
+
* SV-7 (the router same-view test): claude's file-rule grammar has only TWO pattern kinds --
|
|
4
|
+
* `Edit(...)` and `Read(...)`. Dump-confirmed: `ln`'s own dispatch switch has exactly two cases
|
|
5
|
+
* (`case"edit":return tn;case"read":return wt`, each a SINGLE literal tool-name string `ub` filters
|
|
6
|
+
* `ruleValue.toolName` against by exact equality) -- there is no third "write" kind anywhere in the
|
|
7
|
+
* data model. Claude's own WRITE decision function (`zC`) ALWAYS consults `"edit"`-kind rules,
|
|
8
|
+
* regardless of which literal write-shaped tool called it -- this is what makes an `Edit(...)` ask
|
|
9
|
+
* rule fire before a **Write** on claude (the router's own SV-7 measurement), and what makes a
|
|
10
|
+
* `Write(...)`-toolName rule a Winter-only spelling with no claude analogue at all: `ub` filtering
|
|
11
|
+
* on the literal string "Write" never runs, because nothing ever calls it with that string.
|
|
12
|
+
*
|
|
13
|
+
* Every `FILE_RULE_TOOLS` member (grammar.ts) routes to exactly one kind, on every direction (allow,
|
|
14
|
+
* ask, deny alike -- the ruling's own "for both ALLOW and DENY" instruction, extended to ask since
|
|
15
|
+
* ask shares deny's conservative "cross tools" posture throughout this codebase already).
|
|
16
|
+
*/
|
|
17
|
+
export declare function fileRuleKindFor(toolName: string): FileRuleKind | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* The ONE literal tool name a rule must be AUTHORED under to ever be consulted for `kind` -- claude's
|
|
20
|
+
* own `ub` filters `ruleValue.toolName` by EXACT STRING EQUALITY against a single literal per kind
|
|
21
|
+
* (`tn`/`"Edit"` for `"edit"`, `wt`/`"Read"` for `"read"`; dump-confirmed, `ln`'s own two-case
|
|
22
|
+
* switch), never against every tool that happens to share the kind. This is what makes SV-7's
|
|
23
|
+
* "reverse" finding true: a rule AUTHORED as `Write(...)`, `NotebookEdit(...)`, `Glob(...)` or
|
|
24
|
+
* `Grep(...)` is dead code claude never reads for ANY call -- not even a call from that SAME literal
|
|
25
|
+
* tool -- because `ub` was never invoked with that string. `Write`/`NotebookEdit`/`Glob`/`Grep`
|
|
26
|
+
* remain valid rule-authoring tool names SYNTACTICALLY (grammar.ts's `FILE_RULE_TOOLS` still parses
|
|
27
|
+
* them -- Winter does not forbid authoring one), but this function is what `findMatchingFileRuleEntry`
|
|
28
|
+
* (evaluator.ts) filters CANDIDATES with, so only `Edit(...)`/`Read(...)`-authored rules ever reach
|
|
29
|
+
* a group.
|
|
30
|
+
*/
|
|
31
|
+
export declare function canonicalFileRuleAuthoringToolName(kind: FileRuleKind): "Edit" | "Read";
|
|
32
|
+
/** A sentinel distinct from `null` ("resolve against cwd"): a `/`-anchored rule with no resolvable settings-source directory is INERT, never falls back to cwd. */
|
|
33
|
+
declare const INERT_ANCHOR: unique symbol;
|
|
34
|
+
export interface FileRuleAnchor {
|
|
35
|
+
/** The pattern text, relative to `root`, in `ignore`-package (gitignore) grammar. */
|
|
36
|
+
relativePattern: string;
|
|
37
|
+
/** `null` means "resolve against cwd" (`Ma`'s own `P ?? te()`, ported as `root ?? opts.cwd`); the sentinel means the anchor can never match anything. */
|
|
38
|
+
root: string | null | typeof INERT_ANCHOR;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* `jOe` (dump-confirmed): the FOUR anchor spellings WS-07 §3.1 documents, resolved to a
|
|
42
|
+
* `{relativePattern, root}` pair -- ported exactly, including the leading-slash-KEPT behaviour on
|
|
43
|
+
* the three anchored forms (`//x`, `~/x`, `/x`) that is what makes them root-anchored in gitignore
|
|
44
|
+
* terms, and its ABSENCE on `./x`/bare `x` that is what lets `deny ./.env` reach `pkg/.env` (C-1's
|
|
45
|
+
* own example) -- a bare pattern with no leading slash and no inner slash is exactly the shape the
|
|
46
|
+
* `ignore` package's own `^(?=[^^])` -> `(?:^|\/)` replacer un-anchors.
|
|
47
|
+
*
|
|
48
|
+
* A bare `~` (no trailing slash) is NOT specially handled by claude's own `jOe` either -- it falls
|
|
49
|
+
* through to the final else branch as a literal filename pattern `"~"`, cwd-anchored. Ported
|
|
50
|
+
* faithfully rather than "fixed", matching this module's own "port what was measured" discipline.
|
|
51
|
+
*
|
|
52
|
+
* `sourceDir` is claude's `bl(source)` -- the settings-source-derived root for a `/`-anchored rule.
|
|
53
|
+
* Since fix round 11 a settings-tier rule carries it (`SourcedRuleEntry.sourceDir`, set by
|
|
54
|
+
* production-wiring.ts's `buildSettingsRuleSeed` per claude's `Wyt`), and a `/`-anchored rule resolves
|
|
55
|
+
* against it. When it is absent -- a rule from Options, canUseTool or a plugin -- the rule is inert
|
|
56
|
+
* (the `INERT_ANCHOR` root below, treated as "no group to match against" by `matchFileRulesGrouped`).
|
|
57
|
+
*/
|
|
58
|
+
export declare function resolveFileRuleAnchor(pattern: string, opts: {
|
|
59
|
+
home: string;
|
|
60
|
+
sourceDir?: string | undefined;
|
|
61
|
+
}): FileRuleAnchor;
|
|
62
|
+
/**
|
|
63
|
+
* WS-21 fix round 10, item C: a Read/Edit rule's own pattern, resolved to ONE absolute filesystem
|
|
64
|
+
* path -- for the sandbox's own `subpath` rule (sandbox/profile.ts's `denyWritePaths`/
|
|
65
|
+
* `denyReadPaths`/`writableRoots`), which has no glob grammar of its own to hand a pattern string
|
|
66
|
+
* to; a real Seatbelt `subpath` already means "this directory and everything under it," so it needs
|
|
67
|
+
* ONE real path, never a pattern.
|
|
68
|
+
*
|
|
69
|
+
* `undefined` in two cases, matching claude's own observable posture (dump-confirmed, `Jm`: `let{
|
|
70
|
+
* allowOnly:t}=at.getFsWriteConfig();if(t.some(eg))return!0` -- ANY glob-shaped entry in the
|
|
71
|
+
* write-allow set makes claude's OWN sandbox stop trying to restrict writes via that mechanism at
|
|
72
|
+
* all, relying on the separate, glob-aware PERMISSION-RULE layer instead, which is unaffected by
|
|
73
|
+
* this and stays the real enforcement point):
|
|
74
|
+
* - the pattern is INERT (a bare `/`-anchored rule with no resolvable settings-source root --
|
|
75
|
+
* `resolveFileRuleAnchor`'s own pre-existing posture, unchanged here);
|
|
76
|
+
* - the pattern is genuinely GLOB-SHAPED once a single TRAILING `/**` is stripped (redundant with
|
|
77
|
+
* `subpath`'s own "and everything under it" semantics, so it is not itself disqualifying --
|
|
78
|
+
* `Edit(//repo/secrets/**)` becomes the plain path `/repo/secrets`) -- a glob ANYWHERE else
|
|
79
|
+
* (`src/*.ts`, `[wip]`, `a?b`) cannot become one exact path at all.
|
|
80
|
+
* A caller that gets `undefined` back simply does not add this rule to the sandbox's own filesystem
|
|
81
|
+
* lists; the permission-rule layer (`evaluate()`) still enforces it in full, exactly as it always has.
|
|
82
|
+
*/
|
|
83
|
+
export declare function resolveFileRuleAbsolutePath(pattern: string, opts: {
|
|
84
|
+
cwd: string;
|
|
85
|
+
home: string;
|
|
86
|
+
sourceDir?: string;
|
|
87
|
+
}): string | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* WS-21 fix round 11 ("important" item): the sibling of `resolveFileRuleAbsolutePath` that does NOT
|
|
90
|
+
* drop a genuinely glob-shaped pattern -- it resolves the SAME anchor/root as that function but
|
|
91
|
+
* returns the absolute text WITH any remaining glob characters intact (a redundant trailing `/**` is
|
|
92
|
+
* still stripped first, identically, since `subpath`'s/the recursive-regex-suffix's own "and
|
|
93
|
+
* everything under it" semantics already cover it). `undefined` only for the one case that has no
|
|
94
|
+
* absolute form at all -- an INERT `/`-anchored pattern with no resolvable settings-source root,
|
|
95
|
+
* unchanged from `resolveFileRuleAbsolutePath`'s own posture.
|
|
96
|
+
*
|
|
97
|
+
* Exists because claude's own deny-rendering path (`dR`, dump byte 15365699 region) does NOT drop a
|
|
98
|
+
* glob-shaped deny the way `Jm`'s write-ALLOW-only short-circuit does (round 10's own `Jm` finding,
|
|
99
|
+
* `resolveFileRuleAbsolutePath`'s own header) -- a glob-shaped DENY instead becomes an SBPL `(regex
|
|
100
|
+
* ...)` clause (claude's `Li` (dump byte 15365905) / `Rt` (dump byte 15282610)) rather than being silently
|
|
101
|
+
* unenforced by the sandbox layer. Callers check `isGlobShapedFileRulePattern` on the result to
|
|
102
|
+
* decide `subpath` vs a `globToSbplRegexSource`/`recursiveGlobToSbplRegexSource` conversion; ALLOW
|
|
103
|
+
* entries keep using `resolveFileRuleAbsolutePath` (glob-shaped dropped), per the controller's own
|
|
104
|
+
* explicit ruling: "Dropping glob-shaped ALLOW rules stays as it is, because that's stricter."
|
|
105
|
+
*/
|
|
106
|
+
export declare function resolveFileRuleAbsoluteGlobText(pattern: string, opts: {
|
|
107
|
+
cwd: string;
|
|
108
|
+
home: string;
|
|
109
|
+
sourceDir?: string;
|
|
110
|
+
}): string | undefined;
|
|
111
|
+
/**
|
|
112
|
+
* claude's own `Rt` (dump byte 15282610: `e.includes("*")||e.includes("?")||e.includes("[")||e.includes("]")`),
|
|
113
|
+
* confirmed byte-equivalent to this module's own pre-existing glob-char test. Kept as the pure claude
|
|
114
|
+
* primitive; the sandbox DENY split (`splitDenyPathsByGlobShape`/`globDenyEntriesOf`) classifies with
|
|
115
|
+
* `scanDenyPathGlob` below instead (fix round 17, R.3 C-1).
|
|
116
|
+
*/
|
|
117
|
+
export declare function isGlobShapedFileRulePattern(text: string): boolean;
|
|
118
|
+
/**
|
|
119
|
+
* Fix round 12 ("Important" item, claude's own `ed`, dump byte 15367994, found in the SAME chunk as
|
|
120
|
+
* `Ch`/`mR`/`pR` below): every ANCESTOR directory of `path`, walking up via `dirname` until reaching
|
|
121
|
+
* `/` or a fixed point -- does NOT include `path` itself, nor `/`. Feeds the ancestor-rename-bypass
|
|
122
|
+
* fix: claude's own write/read sandbox profiles additionally deny `file-write-unlink`/
|
|
123
|
+
* `file-write-create` on every ancestor of a denied path (and of a glob deny's own fixed prefix), so
|
|
124
|
+
* a sandboxed `mv <ancestor> <elsewhere> && <write inside where it used to be> && mv <elsewhere>
|
|
125
|
+
* <ancestor>` cannot rename the ancestor out of the way and back to slip a write past the deny.
|
|
126
|
+
*/
|
|
127
|
+
export declare function ancestorDirectoriesOf(path: string): string[];
|
|
128
|
+
export declare function globToSbplRegexSource(absoluteGlob: string): string;
|
|
129
|
+
/**
|
|
130
|
+
* claude's own `td` (dump byte 15365977: `Po(e).slice(0,-1)+"(/.*)?$"`) -- `globToSbplRegexSource`'s
|
|
131
|
+
* own whole-string match, WIDENED to also match "the pattern's own match point, optionally followed
|
|
132
|
+
* by `/` and anything deeper" -- the regex equivalent of `subpath`'s own implicit recursive semantics
|
|
133
|
+
* (a plain, non-glob deny already renders as `subpath`, which covers a directory AND everything under
|
|
134
|
+
* it with no extra syntax). claude's own `dR` (the deny-clause builder, same dump region) always uses
|
|
135
|
+
* this recursive form for a glob-shaped deny's OWN base clause -- never the bare `Li`/
|
|
136
|
+
* `globToSbplRegexSource` form, which claude reserves for an ALLOW-carve-out entry nested inside a
|
|
137
|
+
* deny (a feature this port does not carry -- see `resolveFileRuleAbsoluteGlobText`'s own header).
|
|
138
|
+
*/
|
|
139
|
+
export declare function recursiveGlobToSbplRegexSource(absoluteGlob: string): string;
|
|
140
|
+
/**
|
|
141
|
+
* WS-21 fix round 11: the ONE place a plain `sandbox.filesystem.denyWrite`/`denyRead` string list
|
|
142
|
+
* (settings.json's own, user-typed, and `deriveSandboxPathsFromRules`'s own rule-derived denies,
|
|
143
|
+
* which now may ALSO contain glob-shaped text -- see `resolveFileRuleAbsoluteGlobText`'s own header)
|
|
144
|
+
* gets split by glob-shape before reaching `SeatbeltProfileInput`/`RunCommandOptions`: a non-glob
|
|
145
|
+
* entry stays a plain path (`subpath`, unchanged); a glob-shaped one is converted via
|
|
146
|
+
* `recursiveGlobToSbplRegexSource` (the recursive form, matching `subpath`'s own implicit
|
|
147
|
+
* "and everything under it" semantics and claude's own `dR`, which always uses the recursive form for
|
|
148
|
+
* a deny's own base clause). Called once per caller (tools/impl/bash.ts, tools/impl/monitor.ts) --
|
|
149
|
+
* kept as one shared, tested primitive rather than two hand-copies, per this codebase's own
|
|
150
|
+
* "a second copy would be exactly the kind of drift risk this whole phase's review lens exists to
|
|
151
|
+
* catch" precedent (evaluator.ts's `extractCandidateWritePaths`, verbatim).
|
|
152
|
+
*
|
|
153
|
+
* Fix round 12: also returns `globFixedPrefixes` -- for each glob-shaped entry, its OWN canonicalized
|
|
154
|
+
* fixed-prefix directory (claude's own `Rh(u)`, `undefined`/dropped when it resolves to `/`, matching
|
|
155
|
+
* `Ch`'s own `if(p==="/")continue`). Feeds the ancestor-rename-bypass port
|
|
156
|
+
* (`SeatbeltProfileInput.denyWriteGlobFixedPrefixes`/`denyReadGlobFixedPrefixes`, sandbox/profile.ts):
|
|
157
|
+
* the plain `paths` entries need only their OWN ancestors walked (`ed`, `ancestorDirectoriesOf`
|
|
158
|
+
* above) to close the bypass; a glob-shaped deny ALSO needs its fixed prefix walked, and the prefix
|
|
159
|
+
* itself added as a literal deny target (claude's own `Ch` adds both).
|
|
160
|
+
*
|
|
161
|
+
* Fix round 17 (R.3 C-1): classified by `scanDenyPathGlob`, not by claude's `Rt` alone -- an entry
|
|
162
|
+
* whose only glob syntax is one-character bracket classes (`/x/[[]wip] app/.winter/skills`, a literal
|
|
163
|
+
* path spelled for the glob grammar) lands in `paths` UNESCAPED (`/x/[wip] app/.winter/skills`), and a
|
|
164
|
+
* glob entry's fixed prefix runs through such classes. Winter-only hardening (R.3 C-1); claude's
|
|
165
|
+
* `Rt`/`Li` stop at the first `[` -- see `scanDenyPathGlob`'s own header.
|
|
166
|
+
*/
|
|
167
|
+
export declare function splitDenyPathsByGlobShape(paths: readonly string[]): {
|
|
168
|
+
paths: string[];
|
|
169
|
+
regexes: string[];
|
|
170
|
+
globFixedPrefixes: string[];
|
|
171
|
+
};
|
|
172
|
+
/** One glob-shaped deny entry, its recursive SBPL regex source PAIRED with its own fixed-prefix
|
|
173
|
+
* directory -- `splitDenyPathsByGlobShape`'s own `regexes`/`globFixedPrefixes` are two independently
|
|
174
|
+
* FILTERED flat arrays (the latter drops a "/" prefix entirely) with no positional correspondence
|
|
175
|
+
* once any entry is dropped from one but not the other; `fixedPrefix` here is NEVER dropped -- it is
|
|
176
|
+
* always the literal string `"/"` in that case (claude's own `Rh` returns `"/"` too, and `fR`'s own
|
|
177
|
+
* skip/ancestor logic reads that value directly rather than treating "no prefix" as a distinct case). */
|
|
178
|
+
export interface GlobDenyEntry {
|
|
179
|
+
regex: string;
|
|
180
|
+
fixedPrefix: string;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Fix round 13 ("Important" item 1, claude's own `fR`, dump byte 15367091): the PAIRED form
|
|
184
|
+
* `buildReadDenyKeepInPlaceBlock` (sandbox/profile.ts) needs -- see `GlobDenyEntry`'s own header for
|
|
185
|
+
* why `splitDenyPathsByGlobShape`'s own two flat arrays cannot answer this. Scoped to glob-shaped
|
|
186
|
+
* entries only (a plain entry needs no pairing at all -- its own path IS both its recursive-clause
|
|
187
|
+
* anchor and its ancestor-walk root, `buildReadDenyKeepInPlaceBlock` uses `paths` directly for that).
|
|
188
|
+
*/
|
|
189
|
+
export declare function globDenyEntriesOf(paths: readonly string[]): GlobDenyEntry[];
|
|
190
|
+
/**
|
|
191
|
+
* `xi` (dump-confirmed): collapses repeated slashes, and specially handles a LEADING BOM so it
|
|
192
|
+
* cannot accidentally trigger gitignore's own `!`/`#` line-directive meaning (negation/comment). A
|
|
193
|
+
* bare leading BOM with no `!`/`#` after it is DELETED outright (empirically verified: the first of
|
|
194
|
+
* the two `.replace()` calls below always consumes a leading BOM, since `^` in its own regex
|
|
195
|
+
* is unconditional and only the FOLLOWING `[!#]?` is optional -- the second `.replace(/^/,
|
|
196
|
+
* "[]")` is therefore unreachable dead code in the pinned binary's own source whenever the
|
|
197
|
+
* input genuinely starts with a BOM, ported here as harmless dead code too rather than "corrected"
|
|
198
|
+
* into a change of behaviour this module was not asked to make). A leading BOM immediately followed
|
|
199
|
+
* by `!` or `#` becomes an escaped literal `!`/`#` (so the directive character survives as TEXT to
|
|
200
|
+
* match, not as a line-level instruction).
|
|
201
|
+
*/
|
|
202
|
+
export declare function normalizeFileRulePattern(relativePattern: string): string;
|
|
203
|
+
/**
|
|
204
|
+
* `ki` (dump-confirmed): a pattern ending in `/**` is rewritten before being fed to `ignore()`.
|
|
205
|
+
* - For DENY/ASK (`isAllow: false`): the trailing `/**` is simply dropped (`x/**` -> `x`), and the
|
|
206
|
+
* result is left UNANCHORED (no leading `/` added) whenever it already has an inner `/`, is not an
|
|
207
|
+
* allow rule, or already starts with `!`/`#` -- i.e. almost always for deny/ask. This is C-1's own
|
|
208
|
+
* "`ki` turns a deny `x/**` into an unanchored `x`" finding: the bare `x` then matches at ANY
|
|
209
|
+
* depth under the `ignore` package's own un-anchoring rule, covering everything under a directory
|
|
210
|
+
* named `x` anywhere, not merely the literal anchor-relative `x/`.
|
|
211
|
+
* - For ALLOW, when the stripped form has NO inner `/` (a single segment) and does not start with
|
|
212
|
+
* `!`/`#`: the result is instead explicitly re-anchored (`x/**` -> `/x`), so `allow x/**` stays
|
|
213
|
+
* scoped to the anchor root rather than becoming an anywhere-match the way the deny/ask case does.
|
|
214
|
+
* This asymmetry is real and claude's own (not the allow-exact-only asymmetry the controller
|
|
215
|
+
* retired) -- it survives because it is measured, not invented.
|
|
216
|
+
* - A pattern whose stripped form is empty or all-slashes (`/**`, `//**`) is left as `/**`.
|
|
217
|
+
* - Any pattern that does NOT end in `/**` passes through unchanged.
|
|
218
|
+
*/
|
|
219
|
+
export declare function unanchorTrailingDoubleStar(pattern: string, isAllow: boolean): string;
|
|
220
|
+
export interface FileRuleCandidate<TEntry> {
|
|
221
|
+
entry: TEntry;
|
|
222
|
+
/** The rule's own specifier text, exactly as authored (the `jOe`/`xi`/`ki` chain runs on this). */
|
|
223
|
+
pattern: string;
|
|
224
|
+
/** `bl(source)`'s stand-in for a `/`-anchored rule -- see `resolveFileRuleAnchor`'s own header. Absent = every `/`-anchored candidate is inert. */
|
|
225
|
+
sourceDir?: string | undefined;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Item 2 (fix round 9), REVISED by round 10's own item-3 ruling: a malformed pattern's own compile
|
|
229
|
+
* failure is a THROW out of `matchFileRulesGrouped`, not a direction-aware return value. Round 9
|
|
230
|
+
* tried to resolve the failure INSIDE this function (denyAsk -> the broken group's first entry,
|
|
231
|
+
* allow -> null); round 10's controller ruling is that this is not what claude does -- claude's own
|
|
232
|
+
* `Ma` has no per-group catch at all (only the per-TOOL-CALL one far above it, at `d8t`/`ome`'s own
|
|
233
|
+
* boundary), so ONE throwing group aborts the WHOLE permission check for that call, exactly the same
|
|
234
|
+
* way regardless of which direction (deny/ask/allow) was being evaluated when it happened. This
|
|
235
|
+
* class is what makes that propagation typed rather than "any thrown Error" -- caught exactly once,
|
|
236
|
+
* at `evaluator.ts`'s own `evaluate()` (the "decide this one call" boundary), and turned into the
|
|
237
|
+
* generic fail-closed deny `d8t`'s own hardcoded fallback produces.
|
|
238
|
+
*/
|
|
239
|
+
export declare class FileRuleCompileError extends Error {
|
|
240
|
+
constructor(message: string, options?: {
|
|
241
|
+
cause?: unknown;
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* `ln`+`Ma`, combined into one grouped match: builds ONE `ignore()` instance per anchor ROOT from
|
|
246
|
+
* every candidate (see this module's header for why grouping is load-bearing, not cosmetic), then
|
|
247
|
+
* tests `path` against each root's group in turn, returning the FIRST candidate's own `entry` whose
|
|
248
|
+
* group matched -- `null` when nothing matched anywhere. `behavior` is `"allow"` or `"denyAsk"`,
|
|
249
|
+
* mirroring `matchFileRule`'s own existing direction vocabulary (never allow AND denyAsk on the
|
|
250
|
+
* page a caller passed to `ki`, exactly as `ln`'s `r==="allow"` check is exactly `behavior==="allow"`
|
|
251
|
+
* here too).
|
|
252
|
+
*
|
|
253
|
+
* THROWS `FileRuleCompileError` (fix round 10, item 3) when any consulted anchor root's own
|
|
254
|
+
* candidate patterns fail to compile into a real `ignore()` instance -- never resolved to `null`
|
|
255
|
+
* or a particular entry here; see that class's own header for why, and `evaluator.ts`'s `evaluate()`
|
|
256
|
+
* for the one place it is caught.
|
|
257
|
+
*/
|
|
258
|
+
export declare function matchFileRulesGrouped<TEntry>(candidates: readonly FileRuleCandidate<TEntry>[], path: string, opts: {
|
|
259
|
+
cwd: string;
|
|
260
|
+
home: string;
|
|
261
|
+
}, behavior: "allow" | "denyAsk"): TEntry | null;
|
|
262
|
+
/**
|
|
263
|
+
* `Smt`'s own job (round 5's `QCt`): rewrite a path through its REAL prefix (e.g. `/private/tmp/x`, what
|
|
264
|
+
* `realpathSync` actually returns) back to the commonly-typed TRUSTED alias (`/tmp/x`) -- the
|
|
265
|
+
* direction a real, resolved path needs to go to be compared against a rule an author wrote in the
|
|
266
|
+
* short form. A path with no matching real prefix passes through unchanged.
|
|
267
|
+
*/
|
|
268
|
+
export declare function canonicalizeTrustedSymlinkPath(path: string): string;
|
|
269
|
+
/**
|
|
270
|
+
* SV-8 (the router same-view test on the 57e7fef binary): claude's own acceptEdits
|
|
271
|
+
* working-directory boundary check is `sm` (dump-confirmed by content search) -- a plain RELATIVE-
|
|
272
|
+
* PATH PREFIX test, never a compiled glob at all. Winter's own `isWithinBounds` (evaluator.ts) used
|
|
273
|
+
* to reuse the general file-rule matcher with a `"**"` sentinel pattern -- harmless before SV-6/C-1,
|
|
274
|
+
* but once the general matcher started interpreting `[`, `]`, `*` and `\` as glob metacharacters, a
|
|
275
|
+
* cwd or additional-directory root containing any of them (e.g. `[wip] app`) made `"**"` fail to
|
|
276
|
+
* compile the way the caller intended, and acceptEdits asked for every write inside that cwd instead
|
|
277
|
+
* of auto-approving them.
|
|
278
|
+
*
|
|
279
|
+
* This function sidesteps the escaping question SV-8 raises entirely, the same way claude's own `sm`
|
|
280
|
+
* does: a plain path-prefix test never interprets EITHER path as glob syntax, so a root containing a
|
|
281
|
+
* glob-special character needs no escaping here at all -- unlike a real RULE pattern (I-G's own
|
|
282
|
+
* concern), which does.
|
|
283
|
+
*
|
|
284
|
+
* Ported: `caseFold` (default `true`, matching `sm`'s own default and I-D's case-insensitivity
|
|
285
|
+
* finding generally) folds BOTH paths before computing the relative path between them; the macOS
|
|
286
|
+
* `/private/var` -> `/var` and `/private/tmp` -> `/tmp` aliasing is real-symlink-aware -- macOS
|
|
287
|
+
* itself maintains both as symlinks to the `/private/...` originals, so a session cwd resolved
|
|
288
|
+
* through one spelling and a root configured with the other name the SAME real directory (this
|
|
289
|
+
* matters in practice: `os.tmpdir()` on macOS resolves through `/private/var/folders/...`, which is
|
|
290
|
+
* exactly the shape every mkdtemp-based fixture in this codebase's own test suite produces). Not
|
|
291
|
+
* ported: `sm`'s own `uncShapeParity` and `skipPrivateAlias` options (Windows-only concerns) and its
|
|
292
|
+
* `Gn`/`Ha` UNC-path checks -- this codebase supports macOS only (CLAUDE.md's own "latest-OS
|
|
293
|
+
* floors" rule).
|
|
294
|
+
*
|
|
295
|
+
* Fix round 6 (R5-2, the re-review against the pinned 2.1.250 dump): ONLY these TWO pairs -- round
|
|
296
|
+
* 5 widened this to the full six-pair `ni()`/`Sl()` map (`/private/etc`, `/usr/bin`, `/usr/lib`,
|
|
297
|
+
* `/usr/sbin` included), which was WRONG for `sm` specifically: content search against the pinned
|
|
298
|
+
* 2.1.250 dump (not the 2.1.280 build round 5 was cited against) found `sm`'s own alias regexes
|
|
299
|
+
* verbatim -- `g=r?/^\/private\/var\//i:/^\/private\/var\//,w=r?/^\/private\/tmp(\/|$)/i:/^\/private\/
|
|
300
|
+
* tmp(\/|$)/` -- exactly these two, unconditionally, never the wider six-pair set. Reverted to match;
|
|
301
|
+
* the six-pair map (`trustedSymlinkEquivalences`/`canonicalizeTrustedSymlinkPath`) stays, but is now
|
|
302
|
+
* used ONLY by the allow-rule retry in evaluator.ts (`cqe`'s own scope, confirmed at the same dump
|
|
303
|
+
* site), never by this function.
|
|
304
|
+
*/
|
|
305
|
+
export declare function isPathWithinRoot(childPath: string, rootPath: string, opts?: {
|
|
306
|
+
caseFold?: boolean;
|
|
307
|
+
}): boolean;
|
|
308
|
+
/**
|
|
309
|
+
* The traversal fence for a plugin MANIFEST's own declared component paths (`commands`/`agents`/
|
|
310
|
+
* `skills`/`output-styles`/`workflows`/`hooks`).
|
|
311
|
+
*
|
|
312
|
+
* Fix round 6 (R5-1 + a promoted minor, the re-review against the PINNED 2.1.250 dump): claude's own
|
|
313
|
+
* check here is `nV` (dump-confirmed by content search against the pinned dump directly, at the
|
|
314
|
+
* scratchpad path the controller named -- superseding fix round 5's citation of `KGe`/`Aoe` against
|
|
315
|
+
* the INSTALLED 2.1.280 binary, which this round's own ruling says is not the parity authority):
|
|
316
|
+
* `nV(root,entry)` resolves `entry` against `root`, computes `u=path.relative(root,resolved)`, and
|
|
317
|
+
* refuses (`return null`) when `u.startsWith("..")`. Three ways this DIFFERS from `isPathWithinRoot`/
|
|
318
|
+
* `sm` above, all ported exactly rather than reused:
|
|
319
|
+
* 1. CASE-SENSITIVE, always -- `nV`'s own body has no folding call anywhere (confirmed by reading
|
|
320
|
+
* it in full), unlike `sm`'s own `r?/.../i:/.../ ` case-fold branching. Fix round 5's own
|
|
321
|
+
* `resolvesWithinPluginRoot` wrongly delegated to `isPathWithinRoot`'s DEFAULT `caseFold:true`,
|
|
322
|
+
* so on a case-sensitive volume a manifest entry like `../FOO/agents` under a root
|
|
323
|
+
* `.../plugins/foo` was admitted (folded, `FOO` read as `foo`) where claude's own `nV` (and
|
|
324
|
+
* this rewrite) refuses it.
|
|
325
|
+
* 2. NAIVE STRING-PREFIX, not segment-aware -- `u.startsWith("..")` is a bare string test, unlike
|
|
326
|
+
* `sm`'s own `uj` (`/(?:^|[\\/])\.\.(?:[\\/]|$)/`, confirmed by reading ITS full definition too),
|
|
327
|
+
* which requires a `..` SEGMENT bounded by a separator or a string edge. This means a component
|
|
328
|
+
* name that merely STARTS WITH the two characters `..` -- e.g. `"..x/agents"`, a real,
|
|
329
|
+
* non-escaping subdirectory name -- is REFUSED by claude too, not only a genuine `"../"` escape.
|
|
330
|
+
* Matched here rather than "fixed", per the ruling: claude's own inconsistency between its two
|
|
331
|
+
* path-safety mechanisms is not this codebase's to resolve by choosing the more correct one.
|
|
332
|
+
* 3. NO trusted-symlink alias mapping at all -- `nV`'s own body never calls anything resembling
|
|
333
|
+
* `Smt`/`canonicalizeTrustedSymlinkPath`. Moot in practice here regardless, since both operands
|
|
334
|
+
* below are ALREADY realpath'd before this comparison runs (a real, resolved path from
|
|
335
|
+
* `/tmp`/`/var` already comes back in its long `/private/...` form either way).
|
|
336
|
+
*
|
|
337
|
+
* DISCLOSED DIVERGENCE FROM THE PINNED 2.1.250, kept as DELIBERATE HARDENING (the controller's own
|
|
338
|
+
* explicit ruling): `nV` itself is PURELY LEXICAL -- 2.1.250 has no symlink-following/realpath step
|
|
339
|
+
* for a plugin component path at all. This function still realpaths both the candidate and the
|
|
340
|
+
* plugin root first (originally ported from the INSTALLED 2.1.280 binary's own `KGe`/`Aoe`, which DID
|
|
341
|
+
* add this in a build newer than the pin), refusing a symlinked override that points outside the
|
|
342
|
+
* plugin where 2.1.250 would load it -- the safe direction, and it matches claude's own newer
|
|
343
|
+
* behaviour. `nV`'s own comparison shape (case-sensitive, naive-prefix, no alias) is then applied to
|
|
344
|
+
* the REALPATH'D forms rather than to the raw ones `nV` itself compares. `resolveRealTarget` (paths.ts)
|
|
345
|
+
* has the graceful "walk up to the nearest existing ancestor" fallback for a candidate that does not
|
|
346
|
+
* exist YET, and rethrows a non-ENOENT failure (ELOOP on a symlink cycle, EACCES, ...), caught here
|
|
347
|
+
* and treated as a refusal rather than letting a malformed manifest entry crash the whole
|
|
348
|
+
* plugin-loading pass.
|
|
349
|
+
*/
|
|
350
|
+
export declare function resolvesWithinPluginRoot(candidatePath: string, pluginRoot: string): boolean;
|
|
351
|
+
/**
|
|
352
|
+
* Fix round 4 (I-G): a real, resolved filesystem path (e.g. `resolve(winterHome)`) can legitimately
|
|
353
|
+
* contain `[`, `]`, `*` or `\` -- none of which were glob-special under Winter's pre-fix-round-4
|
|
354
|
+
* grammar, but all four are now, since `matchFileRulesGrouped` compiles every pattern through the
|
|
355
|
+
* real `ignore` package. A caller building a rule PATTERN out of a real path (`buildBaselineDenyRules`,
|
|
356
|
+
* engine.ts) must escape these four before interpolating the path into pattern text, or a home
|
|
357
|
+
* directory literally named e.g. `/Users/name[wip]` would have its OWN floor's `[wip]` read back as
|
|
358
|
+
* a character class instead of the four literal characters it names on disk.
|
|
359
|
+
*
|
|
360
|
+
* `?` is DELIBERATELY LEFT RAW, per the controller's own ruling: claude's grammar has no working
|
|
361
|
+
* escape for `?` at all (this module's own `unanchorTrailingDoubleStar`/`\?`-quirk sibling
|
|
362
|
+
* documentation) -- an escaped `\?` would require a literal backslash the real path never has, so it
|
|
363
|
+
* would never match the floor's own intended directory at all. A bare `?` in the pattern instead acts
|
|
364
|
+
* as a single-character wildcard, which still MATCHES a real `?` in the path (a wildcard matches
|
|
365
|
+
* anything, including the literal character) -- over-matching by one character class is the safe
|
|
366
|
+
* direction for a DENY floor (WS-07 §3.1's own posture: a deny that reaches slightly too far is a
|
|
367
|
+
* false-negative-avoiding cost, never a hole), where an escape that matches NOTHING would be a hole.
|
|
368
|
+
*/
|
|
369
|
+
export declare function escapeFileRulePathSegment(path: string): string;
|
|
370
|
+
/**
|
|
371
|
+
* A SINGLE pattern against a SINGLE path -- for a caller that is already iterating rule entries one
|
|
372
|
+
* at a time for a reason unrelated to C-1/SV-7 (e.g. evaluator.ts's own cross-tool
|
|
373
|
+
* `findFileDenyBlockingEdit`, a Winter-invented safety net with no claude analogue: claude's own
|
|
374
|
+
* Write decision never consults `Read(...)` rules at all, so "a Read deny also blocks a Write" is
|
|
375
|
+
* this codebase's own extension, not a ported behaviour). Grouping (this module's own header)
|
|
376
|
+
* therefore does not apply across DIFFERENT callers' unrelated single-pattern checks the way it does
|
|
377
|
+
* within one `matchFileRulesGrouped` call -- this is a thin, no-negation-support convenience, not a
|
|
378
|
+
* second matching engine.
|
|
379
|
+
*/
|
|
380
|
+
export declare function matchesSingleFileRulePattern(pattern: string, path: string, opts: {
|
|
381
|
+
cwd: string;
|
|
382
|
+
home: string;
|
|
383
|
+
}, behavior: "allow" | "denyAsk"): boolean;
|
|
384
|
+
export {};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
export type Specifier = {
|
|
2
|
+
kind: "wildcardAll";
|
|
3
|
+
} | {
|
|
4
|
+
kind: "pattern";
|
|
5
|
+
source: string;
|
|
6
|
+
} | {
|
|
7
|
+
kind: "param";
|
|
8
|
+
field: string;
|
|
9
|
+
value: string | boolean;
|
|
10
|
+
} | {
|
|
11
|
+
kind: "webFetchDomain";
|
|
12
|
+
source: string;
|
|
13
|
+
} | {
|
|
14
|
+
kind: "invalid";
|
|
15
|
+
reason: string;
|
|
16
|
+
};
|
|
17
|
+
export interface ParsedRule {
|
|
18
|
+
toolName: string;
|
|
19
|
+
specifier?: Specifier;
|
|
20
|
+
isBareEquivalent: boolean;
|
|
21
|
+
}
|
|
22
|
+
export declare const PARSE_LIMIT = 50000;
|
|
23
|
+
export declare const READ_ONLY_COMMANDS: ReadonlySet<string>;
|
|
24
|
+
export declare const DANGEROUS_ASSIGNMENT_NAMES: ReadonlySet<string>;
|
|
25
|
+
export declare const FILE_RULE_TOOLS: ReadonlySet<string>;
|
|
26
|
+
export declare function leadingWord(s: string): {
|
|
27
|
+
word: string | undefined;
|
|
28
|
+
afterWord: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Joins backslash-newline continuations the way bash does before it parses: an ODD run of
|
|
32
|
+
* backslashes before a newline ends in a continuation (the last backslash and the newline vanish);
|
|
33
|
+
* an even run is escaped backslashes followed by a real newline. Without this, `echo x >
|
|
34
|
+
* \<newline>.git/config` read its target as `\<newline>.git/config` -- a name with no `.git`
|
|
35
|
+
* segment -- while bash wrote `.git/config` (claude joins them before its own redirect scan).
|
|
36
|
+
*/
|
|
37
|
+
export declare function joinLineContinuations(command: string): string;
|
|
38
|
+
export declare function splitCompound(rawCommand: string): string[] | null;
|
|
39
|
+
export declare function stripWrappers(rawCmd: string, direction: "allow" | "denyAsk"): string;
|
|
40
|
+
/** One word of a command: its text after quote removal, as written, and whether any of it was quoted. */
|
|
41
|
+
export interface ShellWord {
|
|
42
|
+
word: string;
|
|
43
|
+
raw: string;
|
|
44
|
+
quoted: boolean;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* bash's quote removal (and, with `split`, its word splitting at unquoted blanks) over one command's
|
|
48
|
+
* text: single quotes, double quotes (a backslash there drops -- stricter than bash, which keeps it
|
|
49
|
+
* before an ordinary character, and never naming a DIFFERENT protected file), backslash escapes,
|
|
50
|
+
* ANSI-C `$'…'` (decoded) and locale `$"…"` (as double quotes). Expansions are left as written. An
|
|
51
|
+
* unterminated quote runs to the end.
|
|
52
|
+
*/
|
|
53
|
+
export declare function shellWords(s: string, split?: boolean): ShellWord[];
|
|
54
|
+
/** `word` after bash's quote removal, as ONE word (blanks inside it are kept). */
|
|
55
|
+
export declare function dequoteShellWord(word: string): string;
|
|
56
|
+
/** One file-writing redirection: the target word as written (`raw`) and after bash's quote removal. */
|
|
57
|
+
export interface RedirectWrite {
|
|
58
|
+
raw: string;
|
|
59
|
+
target: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Every FILE-writing redirection at the top level of one (sub)command -- the operators bash writes a
|
|
63
|
+
* file through: `>`, `>>`, `>|` (noclobber override), `&>`, `&>>`, `<>` (read-write open), any of them
|
|
64
|
+
* fd-prefixed (`2>`), and `>&word` / `N>&word` whose word is NOT a descriptor (`>&file` is the old
|
|
65
|
+
* spelling of `&>file`; `2>&1`, `>&2`, `>&-` stay descriptor copies). `<<`/`<<<` feed input and
|
|
66
|
+
* `>(`/`<(` are process substitutions -- neither is a file target (the permission layer asks for a
|
|
67
|
+
* process substitution separately). Bash runs without history expansion here, so a target that
|
|
68
|
+
* begins with `!` is ALSO read with the `!` removed (zsh's `>!` clobber), which only adds a path.
|
|
69
|
+
*
|
|
70
|
+
* An unparseable command yields `[]` here; every security caller asks for such a command on its own
|
|
71
|
+
* (`shellWriteConstraint`), because a scan it could not complete proves nothing about its writes.
|
|
72
|
+
*/
|
|
73
|
+
export declare function extractRedirectWrites(rawCommand: string): RedirectWrite[];
|
|
74
|
+
/** `extractRedirectWrites`, target paths only (quotes removed). */
|
|
75
|
+
export declare function extractRedirectTargets(command: string): string[];
|
|
76
|
+
export declare function isRecognizedReadOnly(command: string): boolean;
|
|
77
|
+
export declare function escapeRegExpLiteral(s: string): string;
|
|
78
|
+
/** The call's `url` as a parsed `URL`, or `undefined` when it is absent, not a string, or unparseable. NEVER throws. */
|
|
79
|
+
export declare function webFetchUrlOf(input: Record<string, unknown>): URL | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* The hostname a `WebFetch(domain:...)` rule is compared against: `new URL(url).hostname`, lowercase
|
|
82
|
+
* (the URL parser already lowercases and punycodes it), minus one trailing dot.
|
|
83
|
+
*
|
|
84
|
+
* `undefined` -- which matches NO domain rule, on either direction -- for an absent/unparseable `url`
|
|
85
|
+
* and for a URL with an EMPTY host (`file:///etc/passwd`, `data:`): `domain:*` compiles to a pattern
|
|
86
|
+
* that matches the empty string, so without this an allow rule written for "any website" would
|
|
87
|
+
* pre-approve a URL that names no website at all.
|
|
88
|
+
*
|
|
89
|
+
* The trailing dot is stripped on BOTH sides (here and at parse) because `https://example.com./` is
|
|
90
|
+
* the same host as `https://example.com/` and the URL parser keeps the dot: an exact compare would
|
|
91
|
+
* let that spelling walk past a deny rule.
|
|
92
|
+
*/
|
|
93
|
+
export declare function webFetchHostnameOf(input: Record<string, unknown>): string | undefined;
|
|
94
|
+
/**
|
|
95
|
+
* True when `rule` is a `WebFetch(domain:<host>)` rule that names `hostname` EXACTLY -- no `*`
|
|
96
|
+
* anywhere in it. This is what the evaluator means by "a rule naming that host": a glob
|
|
97
|
+
* (`domain:*`, `domain:*.corp`) matches a host without ever having named it, so it can allow a fetch
|
|
98
|
+
* but can never stand in for the user's consent to one specific address.
|
|
99
|
+
*/
|
|
100
|
+
export declare function isExactWebFetchDomainRule(rule: ParsedRule, hostname: string): boolean;
|
|
101
|
+
export declare function parseRule(raw: string): ParsedRule;
|
|
102
|
+
export declare function matchesRule(rule: ParsedRule, call: {
|
|
103
|
+
toolName: string;
|
|
104
|
+
input: Record<string, unknown>;
|
|
105
|
+
}, opts: {
|
|
106
|
+
direction: "allow" | "denyAsk";
|
|
107
|
+
}): boolean;
|
|
108
|
+
export interface PermissionRuleValidation {
|
|
109
|
+
valid: boolean;
|
|
110
|
+
error?: string;
|
|
111
|
+
suggestion?: string;
|
|
112
|
+
}
|
|
113
|
+
export declare function validatePermissionRuleString(raw: string, direction: "allow" | "deny" | "ask"): PermissionRuleValidation;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { PermissionBehavior } from "@yanlinglabs/winter-agent-sdk";
|
|
2
|
+
export interface MatchFileRuleOptions {
|
|
3
|
+
path: string;
|
|
4
|
+
cwd: string;
|
|
5
|
+
sourceDir?: string;
|
|
6
|
+
home: string;
|
|
7
|
+
direction: "allow" | "denyAsk";
|
|
8
|
+
}
|
|
9
|
+
export declare function resolveTargetPath(path: string, cwd: string): string;
|
|
10
|
+
export declare const MAX_DOUBLE_STARS = 8;
|
|
11
|
+
export declare const MAX_STARS_PER_SEGMENT = 8;
|
|
12
|
+
export declare function exceedsStarsPerSegmentCap(pattern: string): boolean;
|
|
13
|
+
export declare function exceedsDoubleStarCap(pattern: string): boolean;
|
|
14
|
+
export declare function matchFileRule(pattern: string, opts: MatchFileRuleOptions): boolean;
|
|
15
|
+
export interface SymlinkBothEndsResult {
|
|
16
|
+
allowRequiresBoth: boolean;
|
|
17
|
+
denyIfEither: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function resolveRealTarget(path: string): string;
|
|
20
|
+
export declare function resolveSymlinkTargetChain(path: string): string | undefined;
|
|
21
|
+
export declare function checkSymlinkBothEnds(path: string, matcher: (candidatePath: string) => boolean): SymlinkBothEndsResult;
|
|
22
|
+
export declare function matchFileRuleAtBothEnds(pattern: string, opts: MatchFileRuleOptions): boolean;
|
|
23
|
+
export interface FileRuleEntry {
|
|
24
|
+
toolName: string;
|
|
25
|
+
pattern: string;
|
|
26
|
+
behavior: PermissionBehavior;
|
|
27
|
+
sourceDir?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function readDenyBlocksEdit(rules: FileRuleEntry[], path: string, ctx: {
|
|
30
|
+
cwd: string;
|
|
31
|
+
home: string;
|
|
32
|
+
}): boolean;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { PermissionMode, PermissionUpdate, RuleSource } from "@yanlinglabs/winter-agent-sdk";
|
|
2
|
+
import { type SourcedRuleSet } from "./ruleset.js";
|
|
3
|
+
export type { AutoModeConfig } from "./auto/config.js";
|
|
4
|
+
import type { AutoModeConfig } from "./auto/config.js";
|
|
5
|
+
export interface PolicyState {
|
|
6
|
+
mode: PermissionMode;
|
|
7
|
+
version: number;
|
|
8
|
+
rules: SourcedRuleSet;
|
|
9
|
+
autoConfig?: AutoModeConfig;
|
|
10
|
+
}
|
|
11
|
+
export declare class WinterPermissionError extends Error {
|
|
12
|
+
constructor(message: string);
|
|
13
|
+
}
|
|
14
|
+
export declare const PERMISSION_MODES: ReadonlySet<PermissionMode>;
|
|
15
|
+
export declare function isPermissionMode(value: string): value is PermissionMode;
|
|
16
|
+
/**
|
|
17
|
+
* SDK 0.0.16 (P16-7): claude's fork agent (`Ex`) carries `permissionMode: "bubble"` -- NOT a member
|
|
18
|
+
* of `PERMISSION_MODES` and never widened into one (`PermissionMode` is a closed 6-value union read
|
|
19
|
+
* exhaustively elsewhere -- `classifyPermissionMode` in particular). "bubble" is an explicit ALIAS
|
|
20
|
+
* for "no override": the child keeps whatever mode the parent session is CURRENTLY running (never a
|
|
21
|
+
* fixed mode of its own), and its own approval prompts surface through the parent's approval path.
|
|
22
|
+
*
|
|
23
|
+
* The second half needs no new plumbing here -- every child's `PreToolUse`/hook control_requests
|
|
24
|
+
* already forward up to the real host and are answered there (`child-engine.ts`'s
|
|
25
|
+
* `registerChildResponseHandler`/`forwardedHostRequestIds`, Phase 4 Task 8 rider 19); "bubble" only
|
|
26
|
+
* NAMES, deliberately, the mode value that already produces "inherit the parent's live mode" instead
|
|
27
|
+
* of leaving it an accident of an unrecognized permissionMode string falling through
|
|
28
|
+
* `isPermissionMode`'s own false case. `engine.ts`'s `buildChildInheritance` treats this constant
|
|
29
|
+
* (never the general "value is not a known mode" branch) as that alias -- see its own comment.
|
|
30
|
+
*/
|
|
31
|
+
export declare const BUBBLE_PERMISSION_MODE = "bubble";
|
|
32
|
+
export declare function assertKnownPermissionMode(mode: string | undefined): PermissionMode;
|
|
33
|
+
export interface BypassGateConfig {
|
|
34
|
+
allowDangerouslySkipPermissions: boolean;
|
|
35
|
+
disableBypassPermissionsMode: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface SetModeResult {
|
|
38
|
+
ok: true;
|
|
39
|
+
effectiveMode: PermissionMode;
|
|
40
|
+
}
|
|
41
|
+
export interface SetModeError {
|
|
42
|
+
ok: false;
|
|
43
|
+
error: {
|
|
44
|
+
code: string;
|
|
45
|
+
message: string;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export interface ApplyUpdateOk {
|
|
49
|
+
ok: true;
|
|
50
|
+
}
|
|
51
|
+
export declare class PolicyStateStore {
|
|
52
|
+
private state;
|
|
53
|
+
private readonly gate;
|
|
54
|
+
constructor(initial: {
|
|
55
|
+
mode: PermissionMode;
|
|
56
|
+
rules: SourcedRuleSet;
|
|
57
|
+
autoConfig?: AutoModeConfig;
|
|
58
|
+
}, gate: BypassGateConfig);
|
|
59
|
+
getState(): Readonly<PolicyState>;
|
|
60
|
+
setMode(mode: PermissionMode): SetModeResult | SetModeError;
|
|
61
|
+
applyUpdate(update: PermissionUpdate, opts: {
|
|
62
|
+
authority: RuleSource;
|
|
63
|
+
}): ApplyUpdateOk | SetModeError;
|
|
64
|
+
}
|