@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,25 @@
|
|
|
1
|
+
import type { ProtocolSdkMessage as SdkMessage } from "@yanlinglabs/winter-agent-sdk";
|
|
2
|
+
import type { ProviderStreamSink } from "../engine.js";
|
|
3
|
+
export interface StreamFrameSinkDeps {
|
|
4
|
+
sessionId: string;
|
|
5
|
+
/** The pin's own `Options.includePartialMessages`. Absent/false gates `stream_event` OFF and nothing else. */
|
|
6
|
+
includePartialMessages: boolean;
|
|
7
|
+
/** Writes one frame. Never throws to the caller -- see the wrapper below. */
|
|
8
|
+
write: (message: SdkMessage) => void;
|
|
9
|
+
/** Read LIVE, not captured: a `set_model` between generations changes what `reasoning_summary` should name. */
|
|
10
|
+
identity: () => {
|
|
11
|
+
providerId?: string;
|
|
12
|
+
modelKey?: string;
|
|
13
|
+
} | undefined;
|
|
14
|
+
/** The session's CURRENT model, for the same reason. */
|
|
15
|
+
model: () => string;
|
|
16
|
+
/** Injected for deterministic `ttft_ms` in tests. */
|
|
17
|
+
now?: () => number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Builds the sink for ONE generation.
|
|
21
|
+
*
|
|
22
|
+
* Per generation, not per session, because `ttft_ms` is per generation: capture (F) observed exactly
|
|
23
|
+
* two frames carrying it across a run -- one per FORWARDED turn, on that turn's `message_start`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createStreamFrameSink(deps: StreamFrameSinkDeps): ProviderStreamSink;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { CredentialRef } from "@yanlinglabs/winter-agent-sdk";
|
|
2
|
+
import type { CredentialStore } from "@yanlinglabs/winter-provider-runtime";
|
|
3
|
+
import type { KeychainSecretReader } from "./keychain-store.js";
|
|
4
|
+
/**
|
|
5
|
+
* `found` the key, trimmed.
|
|
6
|
+
* `missing` the ref resolves to nothing (no such item, an empty value, `{ kind: "none" }`). The
|
|
7
|
+
* ordinary "no key configured" state -- never an error.
|
|
8
|
+
* `unreadable` something IS there (or the store failed) and it cannot be used as a key. `code` is
|
|
9
|
+
* the store's own vocabulary; `message` names the redacted ref and the reason.
|
|
10
|
+
*/
|
|
11
|
+
export type ToolSecretResult = {
|
|
12
|
+
status: "found";
|
|
13
|
+
key: string;
|
|
14
|
+
} | {
|
|
15
|
+
status: "missing";
|
|
16
|
+
} | {
|
|
17
|
+
status: "unreadable";
|
|
18
|
+
code: "io" | "malformed" | "unsupported";
|
|
19
|
+
message: string;
|
|
20
|
+
};
|
|
21
|
+
/** What an executor is handed. A test fake is a one-line function returning one of the three arms. */
|
|
22
|
+
export type ToolSecretResolver = (ref: CredentialRef) => Promise<ToolSecretResult>;
|
|
23
|
+
export interface ToolSecretResolverDeps {
|
|
24
|
+
/** Serves every NON-keychain ref (`env`, `inline`, `file`, `none`), and keychain refs too when no raw reader is supplied. */
|
|
25
|
+
credentials: CredentialStore;
|
|
26
|
+
/**
|
|
27
|
+
* The keychain's RAW reader (`createKeychainSecretReader`). When present, a keychain ref is read
|
|
28
|
+
* through it EXACTLY ONCE and interpreted here -- never through `credentials.get` first, which
|
|
29
|
+
* would throw `malformed` on a bare key and would cost a second keychain access (each of which can
|
|
30
|
+
* raise an OS consent prompt). ABSENT for a wiring built over an injected store: a test must never
|
|
31
|
+
* reach the real keychain, so an injected store means "this is the only source".
|
|
32
|
+
*/
|
|
33
|
+
readKeychainSecret?: KeychainSecretReader;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* What one stored string means. Exported for its own tests.
|
|
37
|
+
*
|
|
38
|
+
* NOTHING FROM THE STORED VALUE EVER APPEARS IN A MESSAGE -- these messages travel into a tool
|
|
39
|
+
* RESULT, which a model reads. That includes the value of a `kind` field: it is quoted only when it
|
|
40
|
+
* is one of the kinds this SDK writes, because anything else is content of unknown provenance and
|
|
41
|
+
* may be the secret itself (an item holding `{"kind":"sk-live-..."}` must not be echoed).
|
|
42
|
+
*
|
|
43
|
+
* JSON `{ "kind": "api-key", "key": "<non-empty>" }` -> the key
|
|
44
|
+
* a JSON string -> that string, held to the bare-key rule
|
|
45
|
+
* JSON `null` -> missing (a serialised "no value")
|
|
46
|
+
* any other JSON object, array, `true`/`false` -> unreadable: structured, and not an API key
|
|
47
|
+
* text that STARTS like JSON (`{` / `[`) but does not
|
|
48
|
+
* parse -> unreadable. It was meant to be structured;
|
|
49
|
+
* sending the whole blob as the key header
|
|
50
|
+
* would put a broken record on the wire
|
|
51
|
+
* any other text -> the trimmed text, if it has no whitespace
|
|
52
|
+
*
|
|
53
|
+
* A JSON NUMBER is deliberately a key: one made only of digits parses as a number, and it is still
|
|
54
|
+
* the key.
|
|
55
|
+
*/
|
|
56
|
+
export declare function interpretToolSecret(raw: string, locator: string): ToolSecretResult;
|
|
57
|
+
export declare function createToolSecretResolver(deps: ToolSecretResolverDeps): ToolSecretResolver;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ControlResponseFrame } from "@yanlinglabs/winter-agent-sdk";
|
|
2
|
+
import type { FrameSink } from "../protocol/channel.js";
|
|
3
|
+
export interface RpcBridge {
|
|
4
|
+
request<T = unknown>(subtype: string, payload: unknown, opts?: {
|
|
5
|
+
timeoutMs?: number;
|
|
6
|
+
requestId?: string;
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
}): Promise<T>;
|
|
9
|
+
handleResponse(frame: ControlResponseFrame): boolean;
|
|
10
|
+
ownsRequest(requestId: string): boolean;
|
|
11
|
+
rejectAllPending(err: unknown): void;
|
|
12
|
+
cancel(requestId: string): void;
|
|
13
|
+
}
|
|
14
|
+
export declare function createRpcBridge(output: FrameSink): RpcBridge;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { McpServerState, McpServerStateKind, McpServerStateSource } from "../mcp/state.js";
|
|
2
|
+
import type { McpControlSeam } from "../mcp/control-seam.js";
|
|
3
|
+
export type McpControlResult = {
|
|
4
|
+
ok: true;
|
|
5
|
+
payload?: unknown;
|
|
6
|
+
} | {
|
|
7
|
+
ok: false;
|
|
8
|
+
error: {
|
|
9
|
+
code: string;
|
|
10
|
+
message: string;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
export declare function toWireMcpStatus(kind: McpServerStateKind): string;
|
|
14
|
+
export declare function mcpServerStatesToWire(states: readonly McpServerState[]): Array<{
|
|
15
|
+
name: string;
|
|
16
|
+
status: string;
|
|
17
|
+
protocolVersion?: string;
|
|
18
|
+
}>;
|
|
19
|
+
export interface McpControlDeps {
|
|
20
|
+
stateSource?: McpServerStateSource;
|
|
21
|
+
controlSeam?: McpControlSeam;
|
|
22
|
+
}
|
|
23
|
+
export declare function handleMcpStatus(deps: McpControlDeps): Promise<McpControlResult>;
|
|
24
|
+
export declare function handleMcpReconnect(deps: McpControlDeps, payload: unknown): Promise<McpControlResult>;
|
|
25
|
+
export declare function handleMcpToggle(deps: McpControlDeps, payload: unknown): Promise<McpControlResult>;
|
|
26
|
+
export declare function handleMcpSetServers(deps: McpControlDeps, payload: unknown): Promise<McpControlResult>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FrameSource, FrameSink } from "./protocol/channel.js";
|
|
2
|
+
import { type Provider, type ToolExecutor, type SessionPersistence } from "./engine.js";
|
|
3
|
+
export declare function runWinterRuntime(opts: {
|
|
4
|
+
input: FrameSource;
|
|
5
|
+
output: FrameSink;
|
|
6
|
+
provider: Provider;
|
|
7
|
+
sessionId: string;
|
|
8
|
+
cwd: string;
|
|
9
|
+
model: string;
|
|
10
|
+
permissionMode?: string;
|
|
11
|
+
maxTurns?: number;
|
|
12
|
+
tools?: ToolExecutor;
|
|
13
|
+
store?: SessionPersistence;
|
|
14
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { type BrandProfile } from "@yanlinglabs/winter-agent-sdk";
|
|
2
|
+
/**
|
|
3
|
+
* P7a (D19): the two dot-dir names the seatbelt fences. `homeDirName` anchors the winter root under
|
|
4
|
+
* the OS home (the run-dir read deny, the backups write deny, the provider-state read deny);
|
|
5
|
+
* `projectDirName` anchors the per-writable-root control plane (WS-12 §5.2's carve-out).
|
|
6
|
+
*/
|
|
7
|
+
export type SandboxBrand = Pick<BrandProfile, "homeDirName" | "projectDirName">;
|
|
8
|
+
import { type GlobDenyEntry } from "../permissions/file-rules.js";
|
|
9
|
+
export interface SandboxFilesystemSettings {
|
|
10
|
+
allowWrite?: string[];
|
|
11
|
+
denyWrite?: string[];
|
|
12
|
+
allowRead?: string[];
|
|
13
|
+
denyRead?: string[];
|
|
14
|
+
/**
|
|
15
|
+
* Fix round 16, item 2 (claude's own `ag()`, dump-verified: `function ag(){return
|
|
16
|
+
* pe?.filesystem?.allowGitConfig??!1}`): `sandbox.filesystem.allowGitConfig` in settings.json --
|
|
17
|
+
* the ONE settings-facing door for `SeatbeltProfileInput.allowGitConfigWrites`/
|
|
18
|
+
* `RunCommandOptions.allowGitConfigWrites`, which this module and spawn.ts already had (round 15)
|
|
19
|
+
* but nothing set. Wired through `tools/impl/{bash,monitor}.ts`'s own options-builders (each reads
|
|
20
|
+
* `ctx.sandboxSettings.filesystem?.allowGitConfig` directly) -- `buildSeatbeltProfile` itself never
|
|
21
|
+
* reads this field; it is CONSUMED at the caller boundary (spawn.ts's own `allowGitConfigWrites`
|
|
22
|
+
* param), matching every other filesystem key's own "settings shape carries it, a caller resolves
|
|
23
|
+
* it into the profile-builder's own dedicated param" pattern in this file.
|
|
24
|
+
*/
|
|
25
|
+
allowGitConfig?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface SandboxNetworkSettings {
|
|
28
|
+
allowedDomains?: string[];
|
|
29
|
+
deniedDomains?: string[];
|
|
30
|
+
[key: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
export interface SandboxSettings {
|
|
33
|
+
enabled?: boolean;
|
|
34
|
+
autoAllowBashIfSandboxed?: boolean;
|
|
35
|
+
excludedCommands?: string[];
|
|
36
|
+
allowUnsandboxedCommands?: boolean;
|
|
37
|
+
filesystem?: SandboxFilesystemSettings;
|
|
38
|
+
network?: SandboxNetworkSettings;
|
|
39
|
+
}
|
|
40
|
+
export declare const DEFAULT_SANDBOX_SETTINGS: Readonly<SandboxSettings>;
|
|
41
|
+
export declare class SandboxConfigError extends Error {
|
|
42
|
+
constructor(message: string);
|
|
43
|
+
}
|
|
44
|
+
export declare function resolveNetworkPosture(network: SandboxNetworkSettings | undefined): boolean;
|
|
45
|
+
export declare function canonicalizePath(p: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* P7a fix r1 (Important-1): render ONE brand token as a case-insensitive, regex-escaped SBPL literal.
|
|
48
|
+
*
|
|
49
|
+
* The three any-depth control-plane regexes below used to hard-code `[Ww][Ii][Nn][Tt][Ee][Rr]` while
|
|
50
|
+
* the per-root LITERAL denies beside them already derived from `brand.projectDirName`. WS-12 §5.2
|
|
51
|
+
* names those regexes as the closure for the nested-store hole -- a broad writable parent makes
|
|
52
|
+
* `<parent>/proj/<dot-dir>/settings.json` writable with no literal deny for it -- and §5.2 also
|
|
53
|
+
* records that the seatbelt is the ONLY enforcement point left for a bash-invoked write to the
|
|
54
|
+
* permission control plane. Hard-coded, they fenced a directory a reuser's product never reads while
|
|
55
|
+
* leaving the reuser's own control plane open to `echo x > <root>/<nested>/.acme/settings.json`.
|
|
56
|
+
*
|
|
57
|
+
* ESCAPE FIRST, FOLD SECOND, per character: a letter becomes `[Xx]`, and everything else is escaped
|
|
58
|
+
* exactly as `sbplRegexLiteral` escapes it (the brand grammar admits `-` and, for a dot-dir, the
|
|
59
|
+
* leading `.` -- which MUST be escaped or it matches any character). Applied to Winter's own dot-dir
|
|
60
|
+
* it renders `\.[Ww][Ii][Nn][Tt][Ee][Rr]`, byte for byte what the constant it replaces spelled, so
|
|
61
|
+
* the rendered profile is unchanged under `WINTER_BRAND` (a test diffs the whole profile text).
|
|
62
|
+
*
|
|
63
|
+
* Exported so the deny suite can assert the rendering directly rather than by reading the profile.
|
|
64
|
+
*/
|
|
65
|
+
export declare function caseFoldSegment(segment: string): string;
|
|
66
|
+
export interface SeatbeltProfileInput {
|
|
67
|
+
/** The session's own working directory -- always a writable root. */
|
|
68
|
+
cwd: string;
|
|
69
|
+
/** Extra writable subpaths: session scratch (ctx.tempDir), configured filesystem.allowWrite, outputs dir, etc. */
|
|
70
|
+
writableRoots?: string[];
|
|
71
|
+
/** WS-12 §5.3: filesystem.denyWrite, layered AFTER the write-allow block (last-match-wins). */
|
|
72
|
+
denyWritePaths?: string[];
|
|
73
|
+
/** WS-12 §5.3: filesystem.denyRead, layered AFTER the read-allow block (last-match-wins). */
|
|
74
|
+
denyReadPaths?: string[];
|
|
75
|
+
/**
|
|
76
|
+
* Fix round 11 (claude's `Li`/`Rt`, dump byte 15365905/15282610, pinned 2.1.250): glob-shaped
|
|
77
|
+
* deny entries, PRE-CONVERTED by the caller to SBPL regex SOURCE TEXT (`permissions/file-rules.ts`'s
|
|
78
|
+
* `globToSbplRegexSource`/`recursiveGlobToSbplRegexSource`/`splitDenyPathsByGlobShape`) -- this
|
|
79
|
+
* module has no glob grammar of its own (mirrors `denyWritePaths`/`denyReadPaths`'s own "already
|
|
80
|
+
* resolved by the caller" posture) and only quotes/renders. Claude's own macOS sandbox profile
|
|
81
|
+
* builder renders a glob-shaped deny as `(regex ...)` and a plain one as `(subpath ...)` (`Li`); a
|
|
82
|
+
* `subpath` deny alone -- Winter's pre-round-11 posture -- silently drops a glob-shaped Edit deny
|
|
83
|
+
* (e.g. a globstar-anchored `.env` pattern) or `denyWrite` entry from the sandbox layer entirely
|
|
84
|
+
* (the PERMISSION-RULE layer still enforced it for a recognized tool call; a bash-invoked
|
|
85
|
+
* `tee`/`cp` bypassing that layer did not).
|
|
86
|
+
*/
|
|
87
|
+
denyWriteRegexes?: string[];
|
|
88
|
+
denyReadRegexes?: string[];
|
|
89
|
+
/**
|
|
90
|
+
* Fix round 12 ("Important" item, claude's own `Ch`/`ed`/`mR`/`pR`, dump byte 15368116/15367994/
|
|
91
|
+
* 15369065/15368380, pinned 2.1.250): the ancestor-rename-bypass fix. `denyWriteGlobFixedPrefixes`/
|
|
92
|
+
* `denyReadGlobFixedPrefixes` are the CANONICALIZED fixed-prefix directory of each glob-shaped
|
|
93
|
+
* denyWrite/denyRead entry (`permissions/file-rules.ts`'s `splitDenyPathsByGlobShape`, its own
|
|
94
|
+
* `globFixedPrefixes` output) -- this module has no glob grammar of its own, mirrors the other
|
|
95
|
+
* caller-pre-resolved fields above. Combined with `denyWritePaths`/`denyReadPaths` (the PLAIN
|
|
96
|
+
* entries, reused directly -- no new field needed for those), `buildAncestorRenameBypassBlock`
|
|
97
|
+
* below builds a `(deny file-write-unlink file-write-create ...)` clause naming every ANCESTOR of
|
|
98
|
+
* each denied path/glob-fixed-prefix, PLUS the fixed prefix itself, so a sandboxed `mv <ancestor>
|
|
99
|
+
* <elsewhere> && <write inside where it used to be> && mv <elsewhere> <ancestor>` cannot rename an
|
|
100
|
+
* ancestor of a denied path out of the way (and back) to slip a write past the deny.
|
|
101
|
+
*/
|
|
102
|
+
denyWriteGlobFixedPrefixes?: string[];
|
|
103
|
+
denyReadGlobFixedPrefixes?: string[];
|
|
104
|
+
/**
|
|
105
|
+
* Fix round 13 ("Important" item 1, claude's own `fR`, dump byte 15367091): "keep read-denied
|
|
106
|
+
* paths inside write roots in place." Winter's read-deny/write-allow sections previously composed
|
|
107
|
+
* exactly the way claude's OWN `mR`/`pR` alone would -- last-match-wins, and the write-allow
|
|
108
|
+
* (`(allow file-write* (subpath <root>))`) is emitted AFTER the read-deny section, so it silently
|
|
109
|
+
* overrides any read-deny's own implicit protection against being UNLINKED (renamed away): with
|
|
110
|
+
* `Read(.env)` denied and cwd writable, a sandboxed `mv .env x && cat x` renamed the read-denied
|
|
111
|
+
* file to a new, non-denied name and read the secret through it. claude closes this with a THIRD
|
|
112
|
+
* section, `fR`, emitted AFTER the write-allow block: for each read-denied path (or glob-shaped
|
|
113
|
+
* entry, `denyReadGlobEntries` below) that sits inside a write root, denies `file-write-unlink` on
|
|
114
|
+
* its own recursive clause (minus any write root nested INSIDE it, carved back out) and on every
|
|
115
|
+
* one of its ancestor directories that is ALSO inside a write root.
|
|
116
|
+
*/
|
|
117
|
+
denyReadGlobEntries?: GlobDenyEntry[];
|
|
118
|
+
/** Resolved network posture -- see resolveNetworkPosture's own header for why this is a plain boolean here. */
|
|
119
|
+
allowNetwork: boolean;
|
|
120
|
+
/**
|
|
121
|
+
* The real per-user temp dir (`confstr(_CS_DARWIN_USER_TEMP_DIR)`, exposed by `getconf
|
|
122
|
+
* DARWIN_USER_TEMP_DIR` -- resolved by spawn.ts, never by this module, to keep profile.ts
|
|
123
|
+
* platform-free). Omitted entirely -> the mktemp convenience rule is simply not emitted, which is
|
|
124
|
+
* still a CORRECT (if less ergonomic) profile -- mirrors Norma's own "a profile without this
|
|
125
|
+
* convenience rule is still a correct profile" comment.
|
|
126
|
+
*/
|
|
127
|
+
darwinUserTempDir?: string;
|
|
128
|
+
/**
|
|
129
|
+
* WS-12 §2: "the sole baseline read denial is `<home>/<homeDirName>/run`, enforced via profile
|
|
130
|
+
* deny rules layered over allow-read." The daemon's own runtime dir (control socket, PID/lock
|
|
131
|
+
* files) -- a bash-invoked `cat ~/<homeDirName>/run/core.sock` never passes through a read-tool's own
|
|
132
|
+
* permission fence at all (reads are otherwise deliberately unrestricted, per this product's own
|
|
133
|
+
* tool-surface design), so the seatbelt profile is the only enforcement point left. Omitted
|
|
134
|
+
* entirely -> no baseline deny is emitted, still a correct (if less defended) profile -- mirrors
|
|
135
|
+
* `darwinUserTempDir`'s own "omitted is still correct" posture; there is no ToolExecutionContext
|
|
136
|
+
* seam this module can reach into itself (profile.ts stays platform/context-free by design, per
|
|
137
|
+
* this file's own header), so every caller (spawn.ts -> tools/impl/{bash,monitor}.ts) is
|
|
138
|
+
* responsible for threading its own `ctx.home` through.
|
|
139
|
+
*/
|
|
140
|
+
home?: string;
|
|
141
|
+
/**
|
|
142
|
+
* Phase 5 fix wave, I1: the RESOLVED winter root (`<PREFIX>HOME` when set), when it differs
|
|
143
|
+
* from `<home>/<homeDirName>`.
|
|
144
|
+
*
|
|
145
|
+
* `home` above is the OS home and this module appends `brand.homeDirName` to it -- correct only
|
|
146
|
+
* when the resolved root is literally named that. Under a `<PREFIX>HOME` pointing anywhere
|
|
147
|
+
* else, the run read-deny and the backups write-deny both landed on a directory that does not
|
|
148
|
+
* exist while the real one stayed open.
|
|
149
|
+
*
|
|
150
|
+
* ADDED, NEVER SWAPPED: both anchors are emitted, because the carried WS-12 §5.2 deny corpus and
|
|
151
|
+
* every default-home session still assume the literal default, and two denies of overlapping scope
|
|
152
|
+
* cost nothing.
|
|
153
|
+
*/
|
|
154
|
+
winterHome?: string;
|
|
155
|
+
/**
|
|
156
|
+
* WS-21 §3.7/§6.3 item 11: the shared runtime home's durable-paths root (`config.storeHome`),
|
|
157
|
+
* preferred over `winterHome` for the two DURABLE denies below -- the checkpoint (backups) write
|
|
158
|
+
* deny and the provider-state read deny, both of which protect `projects/`-rooted content that
|
|
159
|
+
* lives under the store home once the router links `buildRunHome`, a directory now DISTINCT from
|
|
160
|
+
* the per-run folder `winterHome` names. The run-dir read deny is left anchored on `winterHome`
|
|
161
|
+
* unchanged: it protects the daemon's own `run/` (sockets, pidfiles), which is neither the
|
|
162
|
+
* per-run folder nor the store home in the WS-21 layout, so this module has no better anchor for
|
|
163
|
+
* it than it already had -- a disclosed, unchanged limitation, not a regression.
|
|
164
|
+
*/
|
|
165
|
+
storeHome?: string;
|
|
166
|
+
/**
|
|
167
|
+
* P7a (D19): the brand whose dot-dir and project dot-dir this profile fences.
|
|
168
|
+
*
|
|
169
|
+
* Every winter-owned path segment below is `brand.homeDirName` (the root under the OS home) or
|
|
170
|
+
* `brand.projectDirName` (the per-writable-root control plane). Omitted = `WINTER_BRAND`, so a
|
|
171
|
+
* caller that threads none emits byte-identical SBPL -- which is what the carried WS-12 §5.2 deny
|
|
172
|
+
* corpus and the darwin deny suite assert.
|
|
173
|
+
*/
|
|
174
|
+
brand?: SandboxBrand;
|
|
175
|
+
/**
|
|
176
|
+
* Fix round 15 (claude's own `cR(e=false)` / `mR`'s own `r=false` default parameter): when true,
|
|
177
|
+
* `.git/config` is NOT added to the default write-protected entries (`buildDefaultWriteProtectionBlock`
|
|
178
|
+
* above) -- every OTHER default protection (shell/tool config files, editor/agent dot-dirs,
|
|
179
|
+
* `.git/hooks`) is unaffected; this flag only ever gates `.git/config`, matching `cR`'s own `!e`
|
|
180
|
+
* guard exactly. Omitted = `false` = protected, matching claude's own default. No caller sets this
|
|
181
|
+
* true yet -- kept for parity since claude's own signature carries the knob.
|
|
182
|
+
*/
|
|
183
|
+
allowGitConfigWrites?: boolean;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Build a macOS Seatbelt (SBPL) profile: deny-by-default, read anywhere (minus configured
|
|
187
|
+
* denyRead layers and, when `home` is given, the WS-12 §2 baseline `<home>/<homeDirName>/run` denial --
|
|
188
|
+
* see `SeatbeltProfileInput.home`'s own header), write only under the given roots (minus configured
|
|
189
|
+
* denyWrite layers), network denied unless explicitly allowed.
|
|
190
|
+
*
|
|
191
|
+
* WS-12 §5.2 (verbatim carry, Winter-renamed): EVERY writable root (cwd + each of `writableRoots`)
|
|
192
|
+
* additionally gets an explicit `(deny <ops> (literal "<root>/<projectDir>/<file>"))` line, for
|
|
193
|
+
* each of `permissions.local.json`/`settings.json`/`settings.local.json`, unconditionally, with no
|
|
194
|
+
* opt-in flag to forget -- a bash-invoked `echo x > <projectDir>/permissions.local.json` never passes
|
|
195
|
+
* through a write/edit TOOL's own permission fence at all, so the seatbelt is the only enforcement
|
|
196
|
+
* point left for a shell-invoked write to the permission/settings control plane. SBPL evaluates a
|
|
197
|
+
* profile's rules for a given operation in FILE ORDER, last-match-wins, WITH ONE EMPIRICALLY-VERIFIED
|
|
198
|
+
* EXCEPTION (fix round 14, `WRITE_OPS_SURVIVING_READ_DENY_REPERMIT`'s own header): a clause naming
|
|
199
|
+
* `file-write-unlink`/`file-write-create` EXPLICITLY beats one that only reaches them via the
|
|
200
|
+
* `file-write*` wildcard, independent of file order -- which is why `<ops>` here is
|
|
201
|
+
* `WRITE_OPS_SURVIVING_READ_DENY_REPERMIT` (`file-write* file-write-unlink file-write-create`), not a
|
|
202
|
+
* bare `file-write*`, since round 14 added an EARLIER, blanket, explicit-named re-permit
|
|
203
|
+
* (`buildReadDenyWritePermitBlock`) that a bare-wildcard deny here would no longer survive. Ordinary
|
|
204
|
+
* last-match-wins still governs every OTHER write operation (`file-write-data`, etc.) and governs
|
|
205
|
+
* `<ops>` vs. `<ops>` ties among explicit-named clauses -- placing these denies AFTER the
|
|
206
|
+
* `(allow file-write* (subpath ...))` block carves out exactly these files from an otherwise-writable
|
|
207
|
+
* subpath, without touching a sibling file or an entire OTHER subdirectory like the MEMDIR.
|
|
208
|
+
*
|
|
209
|
+
* A companion `(deny <ops> (regex ...))` per filename, placed after the per-root literals, closes
|
|
210
|
+
* the NESTED-store hole a literal-only deny misses: a broad `writableRoots` entry makes a nested
|
|
211
|
+
* `<root>/projB/<projectDir>/settings.json` writable too, with no literal deny naming that exact
|
|
212
|
+
* path. Case-folded per character (see the module-level regex constants' own comment).
|
|
213
|
+
*/
|
|
214
|
+
export declare function buildSeatbeltProfile(input: SeatbeltProfileInput): string;
|
|
215
|
+
/**
|
|
216
|
+
* Tight Seatbelt profile for a workflow-worker subprocess (WS-11 §1.7's own consumer -- that phase
|
|
217
|
+
* has not shipped a real worker yet; this ships the tested mechanism now, verbatim-ported from
|
|
218
|
+
* Norma's `workflows/sandbox.ts`). Strictly tighter than the ordinary Bash profile:
|
|
219
|
+
* - deny file-write* EVERYWHERE (the worker writes nothing; the journal is appended parent-side),
|
|
220
|
+
* - deny network*,
|
|
221
|
+
* - deny process-fork AND allow process-exec ONLY for the self binary.
|
|
222
|
+
*
|
|
223
|
+
* Part B item 2 (fix wave, P3 close-out) -- THE READ-AXIS CARRY IS NOW CLOSED (Phase 5 Task 3,
|
|
224
|
+
* R5-5: "the P3 worker seatbelt profile PLUS the ledgered run-directory deny"). `opts.home`, when
|
|
225
|
+
* supplied, emits the same baseline `<home>/<homeDirName>/run` read-deny rule the ordinary Bash profile
|
|
226
|
+
* carries (buildSeatbeltProfile's own `home` field), making this profile a strict superset of that
|
|
227
|
+
* one's denials on every axis.
|
|
228
|
+
*
|
|
229
|
+
* `opts` is REQUIRED and `opts.home` is `string | undefined` -- NOT an optional property (fix round
|
|
230
|
+
* 1): "Lane W's spawner must remember to pass it" is exactly the obligation that gets forgotten, and
|
|
231
|
+
* an optional parameter makes forgetting compile. Making the argument mandatory while allowing an
|
|
232
|
+
* explicit `undefined` turns the silent gap into a compile error at every call site, and leaves the
|
|
233
|
+
* genuinely home-less callers (profile.test.ts, the string-shape half of the darwin suite) able to
|
|
234
|
+
* state that they mean it. A profile built with `{ home: undefined }` is still correct, just less
|
|
235
|
+
* defended -- exactly as buildSeatbeltProfile documents for its own `home`.
|
|
236
|
+
*
|
|
237
|
+
* THE #1 RISK (verified empirically by the Norma original): a blanket `(deny process-exec*)` makes
|
|
238
|
+
* sandbox-exec's own execvp() of the target fail ("Operation not permitted"), because the
|
|
239
|
+
* sandbox->target transition is itself an exec checked against the profile. So this allows exec of
|
|
240
|
+
* EXACTLY `selfExecPath` (canonicalized) and nothing else -- enough for the runtime binary to boot
|
|
241
|
+
* (it dyld-loads its libs via the allowed file-read*), while a workflow script still cannot exec
|
|
242
|
+
* /bin/sh etc. Note the operation is `process-fork` (no star) -- `process-fork*` is an unbound
|
|
243
|
+
* variable that fails to load.
|
|
244
|
+
*/
|
|
245
|
+
export declare function buildWorkflowWorkerSeatbeltProfile(selfExecPath: string, opts: {
|
|
246
|
+
home: string | undefined;
|
|
247
|
+
winterHome?: string;
|
|
248
|
+
brand?: SandboxBrand;
|
|
249
|
+
}): string;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { type SandboxBrand, type SandboxSettings } from "./profile.js";
|
|
2
|
+
import type { GlobDenyEntry } from "../permissions/file-rules.js";
|
|
3
|
+
export declare function isSandboxAvailable(checkPath?: string): boolean;
|
|
4
|
+
export declare class SandboxUnavailableError extends Error {
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
export declare function resolveDarwinUserTempDir(): string | null;
|
|
8
|
+
/** Test seam: forget the cached per-user temp dir so a test can observe resolution again. */
|
|
9
|
+
export declare function resetDarwinUserTempDirCacheForTest(): void;
|
|
10
|
+
export type SandboxPosture = "config-disabled" | "override-requested" | "excluded" | "sandboxed" | "unavailable";
|
|
11
|
+
export interface ResolveExecutionPathInput {
|
|
12
|
+
settings: SandboxSettings;
|
|
13
|
+
dangerouslyDisableSandbox?: boolean;
|
|
14
|
+
command: string;
|
|
15
|
+
}
|
|
16
|
+
export interface ExecutionPathDecision {
|
|
17
|
+
posture: Exclude<SandboxPosture, "unavailable">;
|
|
18
|
+
/**
|
|
19
|
+
* WS-12 §4: the call's OWN raw `dangerouslyDisableSandbox` flag, independent of which posture
|
|
20
|
+
* actually won the first-match-wins table below -- so a result can show "the model asked for the
|
|
21
|
+
* override" even on the rare path where `enabled: false` already made it moot (row 1 beats row 2).
|
|
22
|
+
*/
|
|
23
|
+
sandboxOverrideRequested: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* WS-12 §4.1's own table, first-match-wins:
|
|
27
|
+
* 1. `sandbox.enabled === false` -> unsandboxed (config-disabled)
|
|
28
|
+
* 2. `dangerouslyDisableSandbox: true` on the call, UNLESS `allowUnsandboxedCommands === false`
|
|
29
|
+
* -> unsandboxed (override-requested)
|
|
30
|
+
* 3. command matches `excludedCommands` AND `allowUnsandboxedCommands` -> unsandboxed (excluded)
|
|
31
|
+
* 4. otherwise -> sandboxed
|
|
32
|
+
*
|
|
33
|
+
* R3-6 (capture-pending, per this task's own brief): `excludedCommands` matching is EXACT-FULL-
|
|
34
|
+
* COMMAND-STRING equality only -- no trimming, no argv[0] extraction, no prefix rule. WS-12 §12
|
|
35
|
+
* open question 2 leaves the real matching semantics for a future WS-17 differential capture; this
|
|
36
|
+
* is the deliberately narrow placeholder until that capture lands.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveExecutionPath(input: ResolveExecutionPathInput): ExecutionPathDecision;
|
|
39
|
+
export interface RunCommandOptions {
|
|
40
|
+
command: string;
|
|
41
|
+
/**
|
|
42
|
+
* Task 8 (P3 close-out, "excludedCommands raw-match" MUST): the command string `resolveExecutionPath`
|
|
43
|
+
* matches against `settings.excludedCommands`, when it differs from `command` above (the string
|
|
44
|
+
* that actually gets spawned). Defaults to `command` when omitted -- byte-identical to every caller
|
|
45
|
+
* before this field existed (background execution, Monitor's command half, both of which already
|
|
46
|
+
* spawn the model's raw command with no wrapper). bash.ts's own foreground path is the one caller
|
|
47
|
+
* that needs this: it wraps the raw command in a pwd-capture script before spawning (see that
|
|
48
|
+
* file's own `buildPwdCaptureScript`/`runForeground` header comment, "LATENT TRAP") but must match
|
|
49
|
+
* `excludedCommands` against what the model/settings author actually wrote, never the wrapper.
|
|
50
|
+
*/
|
|
51
|
+
matchCommand?: string;
|
|
52
|
+
/** Real, existing, already-canonicalized directory to spawn the shell in. */
|
|
53
|
+
cwd: string;
|
|
54
|
+
env: NodeJS.ProcessEnv;
|
|
55
|
+
timeoutMs: number;
|
|
56
|
+
signal?: AbortSignal;
|
|
57
|
+
onStdout?: (chunk: Buffer) => void;
|
|
58
|
+
onStderr?: (chunk: Buffer) => void;
|
|
59
|
+
/**
|
|
60
|
+
* Fires synchronously once the child is spawned, before any output arrives -- the ONLY way a
|
|
61
|
+
* caller can learn the process-group pid early enough to track/kill it while it is still
|
|
62
|
+
* running (`RunCommandResult.exitCode` etc. only resolve once the process has already exited).
|
|
63
|
+
* `run_in_background` callers (tools/impl/bash.ts) need this to register the task BEFORE
|
|
64
|
+
* awaiting the eventual result; a foreground caller simply omits it.
|
|
65
|
+
*/
|
|
66
|
+
onSpawned?: (info: {
|
|
67
|
+
pid: number;
|
|
68
|
+
}) => void;
|
|
69
|
+
maxStreamedBytes?: number;
|
|
70
|
+
settings: SandboxSettings;
|
|
71
|
+
dangerouslyDisableSandbox?: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* TEST-ONLY injection seam: overrides which path the internal `isSandboxAvailable` check probes
|
|
74
|
+
* for existence, WITHOUT changing the real spawn target (`REAL_SANDBOX_EXEC_PATH` below is always
|
|
75
|
+
* what actually runs once availability passes). WS-12 §3's typed-unavailability path is otherwise
|
|
76
|
+
* unreachable on any dev/CI box that genuinely has /usr/bin/sandbox-exec -- which is every darwin
|
|
77
|
+
* box this product ships on -- so without this seam the throw site below has zero live coverage.
|
|
78
|
+
* Never read from model input (bash.ts's own BashInput has no such field): a model-controllable
|
|
79
|
+
* override of its own sandbox-availability check would be a containment hole, not a test seam.
|
|
80
|
+
*/
|
|
81
|
+
sandboxExecPath?: string;
|
|
82
|
+
/** Extra writable roots beyond cwd -- session scratch, configured filesystem.allowWrite, outputs dir. */
|
|
83
|
+
writableRoots?: string[];
|
|
84
|
+
denyWritePaths?: string[];
|
|
85
|
+
denyReadPaths?: string[];
|
|
86
|
+
/**
|
|
87
|
+
* Fix round 11 (claude's `Li`/`Rt`, dump byte 15365905/15282610): glob-shaped `denyWritePaths`/
|
|
88
|
+
* `denyReadPaths` entries, PRE-CONVERTED to SBPL regex source by the caller (`permissions/
|
|
89
|
+
* file-rules.ts`'s `splitDenyPathsByGlobShape`) -- this module stays glob-grammar-free, exactly
|
|
90
|
+
* like `denyWritePaths`/`denyReadPaths` themselves are already resolved, absolute paths by the
|
|
91
|
+
* time they reach here.
|
|
92
|
+
*/
|
|
93
|
+
denyWriteRegexes?: string[];
|
|
94
|
+
denyReadRegexes?: string[];
|
|
95
|
+
/**
|
|
96
|
+
* Fix round 12 ("Important" item, claude's own `Ch`/`ed`): the ancestor-rename-bypass fix -- each
|
|
97
|
+
* glob-shaped `denyWritePaths`/`denyReadPaths` entry's OWN canonicalized fixed-prefix directory
|
|
98
|
+
* (see `SeatbeltProfileInput.denyWriteGlobFixedPrefixes`'s own header). Same "caller pre-resolves,
|
|
99
|
+
* this module stays glob-grammar-free" posture as `denyWriteRegexes`/`denyReadRegexes` above.
|
|
100
|
+
*/
|
|
101
|
+
denyWriteGlobFixedPrefixes?: string[];
|
|
102
|
+
denyReadGlobFixedPrefixes?: string[];
|
|
103
|
+
/**
|
|
104
|
+
* Fix round 13 ("Important" item 1, claude's own `fR`): the read-deny-keep-in-place fix -- each
|
|
105
|
+
* glob-shaped `denyReadPaths` entry, PAIRED with its own fixed prefix (see
|
|
106
|
+
* `SeatbeltProfileInput.denyReadGlobEntries`'s own header). Read-only -- claude's own `fR` is a
|
|
107
|
+
* read-deny-specific concern.
|
|
108
|
+
*/
|
|
109
|
+
denyReadGlobEntries?: GlobDenyEntry[];
|
|
110
|
+
/**
|
|
111
|
+
* WS-12 §2: the caller's `ctx.home`, threaded straight through to `buildSeatbeltProfile`'s own
|
|
112
|
+
* `home` field for the baseline `<home>/<homeDirName>/run` read denial -- see that field's own header
|
|
113
|
+
* for why this module (rather than profile.ts) is where a real `ctx.home` value gets plugged in.
|
|
114
|
+
* Omitted -> no baseline deny is emitted, same graceful-degradation posture as every other
|
|
115
|
+
* optional profile input here.
|
|
116
|
+
*/
|
|
117
|
+
home?: string;
|
|
118
|
+
/** Phase 5 fix wave, I1: the resolved winter root -- see `SeatbeltProfileInput.winterHome`. */
|
|
119
|
+
winterHome?: string;
|
|
120
|
+
/** WS-21 §3.7: the shared runtime home's durable-paths root -- see `SeatbeltProfileInput.storeHome`. */
|
|
121
|
+
storeHome?: string;
|
|
122
|
+
/** P7a (D19): the session's brand -- the dot-dir names the profile fences. Omitted = `WINTER_BRAND`. */
|
|
123
|
+
brand?: SandboxBrand;
|
|
124
|
+
/** Fix round 15: see `SeatbeltProfileInput.allowGitConfigWrites`'s own header. Omitted = `false`. */
|
|
125
|
+
allowGitConfigWrites?: boolean;
|
|
126
|
+
}
|
|
127
|
+
export interface RunCommandResult {
|
|
128
|
+
exitCode: number | null;
|
|
129
|
+
timedOut: boolean;
|
|
130
|
+
aborted: boolean;
|
|
131
|
+
streamKilled: boolean;
|
|
132
|
+
posture: Exclude<SandboxPosture, "unavailable">;
|
|
133
|
+
sandboxOverrideRequested: boolean;
|
|
134
|
+
/** The generated SBPL profile text, only present when posture === "sandboxed" (debug/test aid). */
|
|
135
|
+
profile?: string;
|
|
136
|
+
/** Set when the child never actually launched (Node's "error" event) -- distinct from a signal-killed exit (exitCode null, this unset). */
|
|
137
|
+
spawnError?: string;
|
|
138
|
+
}
|
|
139
|
+
export declare function runCommand(opts: RunCommandOptions): Promise<RunCommandResult>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export type EnvFilterTier = "user" | "project" | "local" | "flag";
|
|
2
|
+
/**
|
|
3
|
+
* F17 (V8): what a PROJECT or LOCAL tier's `env` block may never set, on top of
|
|
4
|
+
* `ALL_TIER_REFUSED_ENV`. `CLAUDE_CODE_PLUGIN_SEED_DIR`/`CLAUDE_CODE_PROCESS_WRAPPER` are claude's
|
|
5
|
+
* own names (no Winter twin exists); `pluginCacheDirEnvName` is `CLAUDE_CODE_PLUGIN_CACHE_DIR`'s
|
|
6
|
+
* twin, refused at this tier for the identical reason claude refuses the original.
|
|
7
|
+
*/
|
|
8
|
+
export declare const PROJECT_TIER_REFUSED_ENV: readonly string[];
|
|
9
|
+
/**
|
|
10
|
+
* F17's "every tier drops" list, plus every variable ONLY the router may set (spec §3.4.4 step 4 /
|
|
11
|
+
* the Global Constraints list) -- claude-named and Winter-brand-twinned alike. `CLAUDE_CONFIG_DIR`
|
|
12
|
+
* is explicitly HERE despite F17 saying claude itself never drops it (F17 describes claude's OWN
|
|
13
|
+
* base rule; Winter's router additionally refuses it from settings because only the router may ever
|
|
14
|
+
* set the child's config dir -- the Global Constraints list is the authority for that addition).
|
|
15
|
+
*/
|
|
16
|
+
export declare const ALL_TIER_REFUSED_ENV: readonly string[];
|
|
17
|
+
/**
|
|
18
|
+
* F20: once host-managed provider auth is on, every tier's `env` additionally loses every
|
|
19
|
+
* provider/auth/proxy/TLS key -- DISCLOSED, not exhaustively pinned against a live capture (F20's
|
|
20
|
+
* own wording is categorical: "provider, auth, proxy and TLS keys", not a closed name list). Grown
|
|
21
|
+
* by a fixture the way V19's own repo-read enumeration is, rather than guessed complete here.
|
|
22
|
+
*
|
|
23
|
+
* Fix round 1 (Important 3): `/^ANTHROPIC_/` alone caught Anthropic's own `ANTHROPIC_BASE_URL`, but
|
|
24
|
+
* every OTHER provider's base-URL override (`OPENAI_BASE_URL`, `DEEPSEEK_BASE_URL`, ...) sailed
|
|
25
|
+
* through -- F20's own wording ("provider... keys") is provider-AGNOSTIC, and a settings `env`
|
|
26
|
+
* block redirecting ANY provider's endpoint is the identical class of hole a redirected Anthropic
|
|
27
|
+
* endpoint is. `/^CLAUDE_CODE_USE_/` (+ its Winter-brand twin) closes the PROVIDER-SWITCH class
|
|
28
|
+
* (claude's own `CLAUDE_CODE_USE_BEDROCK`/`CLAUDE_CODE_USE_VERTEX`-shaped variables): flipping which
|
|
29
|
+
* backend a host-managed session talks to is an endpoint redirect by another name.
|
|
30
|
+
*/
|
|
31
|
+
export declare const HOST_MANAGED_REFUSED_ENV_PATTERNS: readonly RegExp[];
|
|
32
|
+
/**
|
|
33
|
+
* `Settings` KEYS (not env vars) that host-managed mode disables (F20: "disables apiKeyHelper/
|
|
34
|
+
* awsAuthRefresh/awsCredentialExport"). Only `apiKeyHelper` is modelled in Winter's own `Settings`
|
|
35
|
+
* shape today (`packages/sdk/src/settings/types.ts:95`) -- claude's `awsAuthRefresh`/
|
|
36
|
+
* `awsCredentialExport` have no Winter field to disable yet, so there is nothing to add for them
|
|
37
|
+
* until one exists.
|
|
38
|
+
*/
|
|
39
|
+
export declare const HOST_MANAGED_DISABLED_SETTINGS_KEYS: readonly string[];
|
|
40
|
+
/**
|
|
41
|
+
* Filters one settings tier's `env` block before it reaches the child's process env. `undefined`
|
|
42
|
+
* input (no `env` block at all) yields `{}`, never a throw.
|
|
43
|
+
*/
|
|
44
|
+
export declare function filterSettingsEnv(env: Record<string, string> | undefined, tier: EnvFilterTier, opts: {
|
|
45
|
+
hostManaged: boolean;
|
|
46
|
+
}): Record<string, string>;
|
|
47
|
+
/**
|
|
48
|
+
* F20's settings-KEY half of host-managed mode: strips `HOST_MANAGED_DISABLED_SETTINGS_KEYS` from a
|
|
49
|
+
* settings-shaped object when `hostManaged` is on. Generic over the object shape (`production-
|
|
50
|
+
* wiring.ts` applies it to a tier's resolved `Settings`, never re-deriving the key list itself).
|
|
51
|
+
*/
|
|
52
|
+
export declare function applyHostManagedSettingsFilter(settings: Record<string, unknown>, hostManaged: boolean): Record<string, unknown>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type HookEntriesFromSettings, type SettingsHookSourceInput } from "../../hooks/from-config.js";
|
|
2
|
+
import type { PluginBundle } from "../../plugins/bundle.js";
|
|
3
|
+
/**
|
|
4
|
+
* The `HookSource` a plugin's hooks are filed under.
|
|
5
|
+
*
|
|
6
|
+
* WAS `sdk`, A DISCLOSED STAND-IN -- Phase 5 Task 8 (rider 19) added the real `plugin` member to
|
|
7
|
+
* `HookSource`, closing this lane's own NEEDS_CONTEXT 4. The stand-in's visible cost was that a
|
|
8
|
+
* plugin hook was indistinguishable from an `Options.hooks` registration in an audit record's
|
|
9
|
+
* `source` field; it now names itself.
|
|
10
|
+
*
|
|
11
|
+
* UNGATED by workspace trust, unchanged: a plugin is loaded because a decision was made outside the
|
|
12
|
+
* repository, so gating it on which directory the session is in is neither the pin's model nor
|
|
13
|
+
* Winter's (`subagents/definitions.ts` records the identical reasoning for `pluginAgents`). It ranks
|
|
14
|
+
* LAST in the merge order (`SOURCE_RANK.plugin = 5`, after `sdk`) -- a plugin ships defaults every
|
|
15
|
+
* more-specific source may override.
|
|
16
|
+
*/
|
|
17
|
+
export declare const PLUGIN_HOOK_SOURCE: "plugin";
|
|
18
|
+
/** The `resolveSettingsDetailed` / pinned `ResolvedSettings` shapes this accepts -- either field. */
|
|
19
|
+
export interface ResolvedSettingsHookInput {
|
|
20
|
+
perSource?: readonly SettingsHookSourceInput[] | undefined;
|
|
21
|
+
sources?: readonly SettingsHookSourceInput[] | undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Project a settings resolution onto `buildHookEntriesFromSettings`' input.
|
|
25
|
+
*
|
|
26
|
+
* `perSource` is preferred (it is a superset carrying `loaded`/`error`); the pinned `sources` array
|
|
27
|
+
* is the fallback so a caller holding only a `ResolvedSettings` can still produce hooks. Both are
|
|
28
|
+
* already highest-precedence-first, and this preserves that order -- the registry's own stable sort
|
|
29
|
+
* uses registration order as its within-source tiebreak.
|
|
30
|
+
*/
|
|
31
|
+
export declare function settingsHookSourceInputs(resolved: ResolvedSettingsHookInput | undefined): SettingsHookSourceInput[];
|
|
32
|
+
/**
|
|
33
|
+
* Turn every loaded plugin's manifest `hooks` block into real entries.
|
|
34
|
+
*
|
|
35
|
+
* ONE `buildHookEntriesFromSettings` CALL PER PLUGIN, then the ids are re-stamped with the plugin's
|
|
36
|
+
* name. That is not tidiness -- it is a correctness requirement. That function derives an id
|
|
37
|
+
* positionally, `${event}:${source}:${groupIndex}:${hookIndex}`, which is deterministic on both
|
|
38
|
+
* sides of the wire and therefore IDENTICAL for two different plugins whose blocks have the same
|
|
39
|
+
* shape. `createCommandHookInvoker` builds `commandsById` as a `Map`, so two colliding ids would
|
|
40
|
+
* leave the LAST plugin's command answering for both entries -- one plugin's hook silently running
|
|
41
|
+
* another plugin's shell command. Prefixing with the plugin name also keeps a plugin id disjoint
|
|
42
|
+
* from every `Options.hooks` id, which shares the same `sdk` source.
|
|
43
|
+
*/
|
|
44
|
+
export declare function pluginHookEntries(bundles: readonly PluginBundle[]): HookEntriesFromSettings;
|