@chances-ai/engine 24.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/discover.d.ts +30 -0
- package/dist/agents/discover.d.ts.map +1 -0
- package/dist/agents/discover.js +183 -0
- package/dist/agents/discover.js.map +1 -0
- package/dist/agents/index.d.ts +20 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +52 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/parse.d.ts +61 -0
- package/dist/agents/parse.d.ts.map +1 -0
- package/dist/agents/parse.js +527 -0
- package/dist/agents/parse.js.map +1 -0
- package/dist/agents/types.d.ts +52 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +8 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/ai/adapters/ai-sdk-stream.d.ts +19 -0
- package/dist/ai/adapters/ai-sdk-stream.d.ts.map +1 -0
- package/dist/ai/adapters/ai-sdk-stream.js +125 -0
- package/dist/ai/adapters/ai-sdk-stream.js.map +1 -0
- package/dist/ai/adapters/ai-sdk.d.ts +56 -0
- package/dist/ai/adapters/ai-sdk.d.ts.map +1 -0
- package/dist/ai/adapters/ai-sdk.js +112 -0
- package/dist/ai/adapters/ai-sdk.js.map +1 -0
- package/dist/ai/adapters/mock.d.ts +13 -0
- package/dist/ai/adapters/mock.d.ts.map +1 -0
- package/dist/ai/adapters/mock.js +54 -0
- package/dist/ai/adapters/mock.js.map +1 -0
- package/dist/ai/adapters/openai-compatible.d.ts +23 -0
- package/dist/ai/adapters/openai-compatible.d.ts.map +1 -0
- package/dist/ai/adapters/openai-compatible.js +45 -0
- package/dist/ai/adapters/openai-compatible.js.map +1 -0
- package/dist/ai/cost.d.ts +3 -0
- package/dist/ai/cost.d.ts.map +1 -0
- package/dist/ai/cost.js +5 -0
- package/dist/ai/cost.js.map +1 -0
- package/dist/ai/index.d.ts +12 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +11 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/known-models.d.ts +20 -0
- package/dist/ai/known-models.d.ts.map +1 -0
- package/dist/ai/known-models.js +129 -0
- package/dist/ai/known-models.js.map +1 -0
- package/dist/ai/registry.d.ts +12 -0
- package/dist/ai/registry.d.ts.map +1 -0
- package/dist/ai/registry.js +24 -0
- package/dist/ai/registry.js.map +1 -0
- package/dist/ai/retry.d.ts +11 -0
- package/dist/ai/retry.d.ts.map +1 -0
- package/dist/ai/retry.js +14 -0
- package/dist/ai/retry.js.map +1 -0
- package/dist/ai/router.d.ts +25 -0
- package/dist/ai/router.d.ts.map +1 -0
- package/dist/ai/router.js +36 -0
- package/dist/ai/router.js.map +1 -0
- package/dist/ai/setup.d.ts +23 -0
- package/dist/ai/setup.d.ts.map +1 -0
- package/dist/ai/setup.js +47 -0
- package/dist/ai/setup.js.map +1 -0
- package/dist/ai/summarizer.d.ts +24 -0
- package/dist/ai/summarizer.d.ts.map +1 -0
- package/dist/ai/summarizer.js +56 -0
- package/dist/ai/summarizer.js.map +1 -0
- package/dist/ai/types.d.ts +83 -0
- package/dist/ai/types.d.ts.map +1 -0
- package/dist/ai/types.js +2 -0
- package/dist/ai/types.js.map +1 -0
- package/dist/core/compaction/circuit-breaker.d.ts +32 -0
- package/dist/core/compaction/circuit-breaker.d.ts.map +1 -0
- package/dist/core/compaction/circuit-breaker.js +42 -0
- package/dist/core/compaction/circuit-breaker.js.map +1 -0
- package/dist/core/compaction/compactor.d.ts +75 -0
- package/dist/core/compaction/compactor.d.ts.map +1 -0
- package/dist/core/compaction/compactor.js +261 -0
- package/dist/core/compaction/compactor.js.map +1 -0
- package/dist/core/compaction/estimate.d.ts +39 -0
- package/dist/core/compaction/estimate.d.ts.map +1 -0
- package/dist/core/compaction/estimate.js +74 -0
- package/dist/core/compaction/estimate.js.map +1 -0
- package/dist/core/compaction/index.d.ts +5 -0
- package/dist/core/compaction/index.d.ts.map +1 -0
- package/dist/core/compaction/index.js +5 -0
- package/dist/core/compaction/index.js.map +1 -0
- package/dist/core/compaction/prune.d.ts +43 -0
- package/dist/core/compaction/prune.d.ts.map +1 -0
- package/dist/core/compaction/prune.js +51 -0
- package/dist/core/compaction/prune.js.map +1 -0
- package/dist/core/engine.d.ts +268 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +767 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +6 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/task-tool.d.ts +175 -0
- package/dist/core/task-tool.d.ts.map +1 -0
- package/dist/core/task-tool.js +901 -0
- package/dist/core/task-tool.js.map +1 -0
- package/dist/core/workspace-query.d.ts +83 -0
- package/dist/core/workspace-query.d.ts.map +1 -0
- package/dist/core/workspace-query.js +217 -0
- package/dist/core/workspace-query.js.map +1 -0
- package/dist/core/worktree/active-marker.d.ts +31 -0
- package/dist/core/worktree/active-marker.d.ts.map +1 -0
- package/dist/core/worktree/active-marker.js +109 -0
- package/dist/core/worktree/active-marker.js.map +1 -0
- package/dist/core/worktree/create.d.ts +40 -0
- package/dist/core/worktree/create.d.ts.map +1 -0
- package/dist/core/worktree/create.js +121 -0
- package/dist/core/worktree/create.js.map +1 -0
- package/dist/core/worktree/errors.d.ts +7 -0
- package/dist/core/worktree/errors.d.ts.map +1 -0
- package/dist/core/worktree/errors.js +11 -0
- package/dist/core/worktree/errors.js.map +1 -0
- package/dist/core/worktree/gc.d.ts +39 -0
- package/dist/core/worktree/gc.d.ts.map +1 -0
- package/dist/core/worktree/gc.js +146 -0
- package/dist/core/worktree/gc.js.map +1 -0
- package/dist/core/worktree/git.d.ts +53 -0
- package/dist/core/worktree/git.d.ts.map +1 -0
- package/dist/core/worktree/git.js +166 -0
- package/dist/core/worktree/git.js.map +1 -0
- package/dist/core/worktree/index.d.ts +8 -0
- package/dist/core/worktree/index.d.ts.map +1 -0
- package/dist/core/worktree/index.js +8 -0
- package/dist/core/worktree/index.js.map +1 -0
- package/dist/core/worktree/paths.d.ts +26 -0
- package/dist/core/worktree/paths.d.ts.map +1 -0
- package/dist/core/worktree/paths.js +57 -0
- package/dist/core/worktree/paths.js.map +1 -0
- package/dist/core/worktree/slug.d.ts +6 -0
- package/dist/core/worktree/slug.d.ts.map +1 -0
- package/dist/core/worktree/slug.js +21 -0
- package/dist/core/worktree/slug.js.map +1 -0
- package/dist/local-vault/file-store.d.ts +64 -0
- package/dist/local-vault/file-store.d.ts.map +1 -0
- package/dist/local-vault/file-store.js +225 -0
- package/dist/local-vault/file-store.js.map +1 -0
- package/dist/local-vault/index.d.ts +57 -0
- package/dist/local-vault/index.d.ts.map +1 -0
- package/dist/local-vault/index.js +68 -0
- package/dist/local-vault/index.js.map +1 -0
- package/dist/local-vault/keychain.d.ts +58 -0
- package/dist/local-vault/keychain.d.ts.map +1 -0
- package/dist/local-vault/keychain.js +200 -0
- package/dist/local-vault/keychain.js.map +1 -0
- package/dist/local-vault/mutex.d.ts +20 -0
- package/dist/local-vault/mutex.d.ts.map +1 -0
- package/dist/local-vault/mutex.js +44 -0
- package/dist/local-vault/mutex.js.map +1 -0
- package/dist/local-vault/passphrase.d.ts +30 -0
- package/dist/local-vault/passphrase.d.ts.map +1 -0
- package/dist/local-vault/passphrase.js +72 -0
- package/dist/local-vault/passphrase.js.map +1 -0
- package/dist/lsp/config.d.ts +34 -0
- package/dist/lsp/config.d.ts.map +1 -0
- package/dist/lsp/config.js +68 -0
- package/dist/lsp/config.js.map +1 -0
- package/dist/lsp/detect.d.ts +7 -0
- package/dist/lsp/detect.d.ts.map +1 -0
- package/dist/lsp/detect.js +78 -0
- package/dist/lsp/detect.js.map +1 -0
- package/dist/lsp/errors.d.ts +11 -0
- package/dist/lsp/errors.d.ts.map +1 -0
- package/dist/lsp/errors.js +11 -0
- package/dist/lsp/errors.js.map +1 -0
- package/dist/lsp/formatters.d.ts +147 -0
- package/dist/lsp/formatters.d.ts.map +1 -0
- package/dist/lsp/formatters.js +259 -0
- package/dist/lsp/formatters.js.map +1 -0
- package/dist/lsp/index.d.ts +31 -0
- package/dist/lsp/index.d.ts.map +1 -0
- package/dist/lsp/index.js +31 -0
- package/dist/lsp/index.js.map +1 -0
- package/dist/lsp/instance.d.ts +72 -0
- package/dist/lsp/instance.d.ts.map +1 -0
- package/dist/lsp/instance.js +489 -0
- package/dist/lsp/instance.js.map +1 -0
- package/dist/lsp/lazy-load.d.ts +27 -0
- package/dist/lsp/lazy-load.d.ts.map +1 -0
- package/dist/lsp/lazy-load.js +57 -0
- package/dist/lsp/lazy-load.js.map +1 -0
- package/dist/lsp/manager.d.ts +59 -0
- package/dist/lsp/manager.d.ts.map +1 -0
- package/dist/lsp/manager.js +242 -0
- package/dist/lsp/manager.js.map +1 -0
- package/dist/lsp/ops.d.ts +13 -0
- package/dist/lsp/ops.d.ts.map +1 -0
- package/dist/lsp/ops.js +225 -0
- package/dist/lsp/ops.js.map +1 -0
- package/dist/lsp/rpc.d.ts +47 -0
- package/dist/lsp/rpc.d.ts.map +1 -0
- package/dist/lsp/rpc.js +134 -0
- package/dist/lsp/rpc.js.map +1 -0
- package/dist/lsp/safe-uri.d.ts +18 -0
- package/dist/lsp/safe-uri.d.ts.map +1 -0
- package/dist/lsp/safe-uri.js +96 -0
- package/dist/lsp/safe-uri.js.map +1 -0
- package/dist/lsp/types.d.ts +70 -0
- package/dist/lsp/types.d.ts.map +1 -0
- package/dist/lsp/types.js +16 -0
- package/dist/lsp/types.js.map +1 -0
- package/dist/mcp/bridge.d.ts +57 -0
- package/dist/mcp/bridge.d.ts.map +1 -0
- package/dist/mcp/bridge.js +98 -0
- package/dist/mcp/bridge.js.map +1 -0
- package/dist/mcp/category.d.ts +22 -0
- package/dist/mcp/category.d.ts.map +1 -0
- package/dist/mcp/category.js +11 -0
- package/dist/mcp/category.js.map +1 -0
- package/dist/mcp/client.d.ts +228 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +352 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/content.d.ts +86 -0
- package/dist/mcp/content.d.ts.map +1 -0
- package/dist/mcp/content.js +147 -0
- package/dist/mcp/content.js.map +1 -0
- package/dist/mcp/env.d.ts +19 -0
- package/dist/mcp/env.d.ts.map +1 -0
- package/dist/mcp/env.js +50 -0
- package/dist/mcp/env.js.map +1 -0
- package/dist/mcp/host.d.ts +199 -0
- package/dist/mcp/host.d.ts.map +1 -0
- package/dist/mcp/host.js +530 -0
- package/dist/mcp/host.js.map +1 -0
- package/dist/mcp/index.d.ts +18 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +17 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/load-mcp-host.d.ts +32 -0
- package/dist/mcp/load-mcp-host.d.ts.map +1 -0
- package/dist/mcp/load-mcp-host.js +49 -0
- package/dist/mcp/load-mcp-host.js.map +1 -0
- package/dist/mcp/oauth/callback-server.d.ts +73 -0
- package/dist/mcp/oauth/callback-server.d.ts.map +1 -0
- package/dist/mcp/oauth/callback-server.js +280 -0
- package/dist/mcp/oauth/callback-server.js.map +1 -0
- package/dist/mcp/oauth/config-hash.d.ts +24 -0
- package/dist/mcp/oauth/config-hash.d.ts.map +1 -0
- package/dist/mcp/oauth/config-hash.js +55 -0
- package/dist/mcp/oauth/config-hash.js.map +1 -0
- package/dist/mcp/oauth/error-normalize.d.ts +39 -0
- package/dist/mcp/oauth/error-normalize.d.ts.map +1 -0
- package/dist/mcp/oauth/error-normalize.js +91 -0
- package/dist/mcp/oauth/error-normalize.js.map +1 -0
- package/dist/mcp/oauth/provider.d.ts +190 -0
- package/dist/mcp/oauth/provider.d.ts.map +1 -0
- package/dist/mcp/oauth/provider.js +305 -0
- package/dist/mcp/oauth/provider.js.map +1 -0
- package/dist/mcp/oauth/refresh-coalescer.d.ts +46 -0
- package/dist/mcp/oauth/refresh-coalescer.d.ts.map +1 -0
- package/dist/mcp/oauth/refresh-coalescer.js +77 -0
- package/dist/mcp/oauth/refresh-coalescer.js.map +1 -0
- package/dist/mcp/oauth/sdk-shapes.d.ts +77 -0
- package/dist/mcp/oauth/sdk-shapes.d.ts.map +1 -0
- package/dist/mcp/oauth/sdk-shapes.js +21 -0
- package/dist/mcp/oauth/sdk-shapes.js.map +1 -0
- package/dist/mcp/parse.d.ts +28 -0
- package/dist/mcp/parse.d.ts.map +1 -0
- package/dist/mcp/parse.js +209 -0
- package/dist/mcp/parse.js.map +1 -0
- package/dist/mcp/prompts.d.ts +31 -0
- package/dist/mcp/prompts.d.ts.map +1 -0
- package/dist/mcp/prompts.js +71 -0
- package/dist/mcp/prompts.js.map +1 -0
- package/dist/mcp/redact.d.ts +62 -0
- package/dist/mcp/redact.d.ts.map +1 -0
- package/dist/mcp/redact.js +87 -0
- package/dist/mcp/redact.js.map +1 -0
- package/dist/mcp/resources.d.ts +70 -0
- package/dist/mcp/resources.d.ts.map +1 -0
- package/dist/mcp/resources.js +170 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/types.d.ts +123 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +2 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/memory/frontmatter.d.ts +18 -0
- package/dist/memory/frontmatter.d.ts.map +1 -0
- package/dist/memory/frontmatter.js +81 -0
- package/dist/memory/frontmatter.js.map +1 -0
- package/dist/memory/index.d.ts +5 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +5 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/store.d.ts +44 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +237 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/memory/tools.d.ts +11 -0
- package/dist/memory/tools.d.ts.map +1 -0
- package/dist/memory/tools.js +159 -0
- package/dist/memory/tools.js.map +1 -0
- package/dist/memory/types.d.ts +32 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js +20 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/plugin-api/index.d.ts +167 -0
- package/dist/plugin-api/index.d.ts.map +1 -0
- package/dist/plugin-api/index.js +151 -0
- package/dist/plugin-api/index.js.map +1 -0
- package/dist/plugin-logger/index.d.ts +21 -0
- package/dist/plugin-logger/index.d.ts.map +1 -0
- package/dist/plugin-logger/index.js +59 -0
- package/dist/plugin-logger/index.js.map +1 -0
- package/dist/session/index.d.ts +125 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +202 -0
- package/dist/session/index.js.map +1 -0
- package/dist/tools/approval.d.ts +33 -0
- package/dist/tools/approval.d.ts.map +1 -0
- package/dist/tools/approval.js +53 -0
- package/dist/tools/approval.js.map +1 -0
- package/dist/tools/builtins/_shared.d.ts +94 -0
- package/dist/tools/builtins/_shared.d.ts.map +1 -0
- package/dist/tools/builtins/_shared.js +246 -0
- package/dist/tools/builtins/_shared.js.map +1 -0
- package/dist/tools/builtins/ask-user-question.d.ts +27 -0
- package/dist/tools/builtins/ask-user-question.d.ts.map +1 -0
- package/dist/tools/builtins/ask-user-question.js +191 -0
- package/dist/tools/builtins/ask-user-question.js.map +1 -0
- package/dist/tools/builtins/bash.d.ts +3 -0
- package/dist/tools/builtins/bash.d.ts.map +1 -0
- package/dist/tools/builtins/bash.js +158 -0
- package/dist/tools/builtins/bash.js.map +1 -0
- package/dist/tools/builtins/diff.d.ts +3 -0
- package/dist/tools/builtins/diff.d.ts.map +1 -0
- package/dist/tools/builtins/diff.js +83 -0
- package/dist/tools/builtins/diff.js.map +1 -0
- package/dist/tools/builtins/edit.d.ts +3 -0
- package/dist/tools/builtins/edit.d.ts.map +1 -0
- package/dist/tools/builtins/edit.js +40 -0
- package/dist/tools/builtins/edit.js.map +1 -0
- package/dist/tools/builtins/glob.d.ts +3 -0
- package/dist/tools/builtins/glob.d.ts.map +1 -0
- package/dist/tools/builtins/glob.js +37 -0
- package/dist/tools/builtins/glob.js.map +1 -0
- package/dist/tools/builtins/grep.d.ts +3 -0
- package/dist/tools/builtins/grep.d.ts.map +1 -0
- package/dist/tools/builtins/grep.js +81 -0
- package/dist/tools/builtins/grep.js.map +1 -0
- package/dist/tools/builtins/lsp.d.ts +3 -0
- package/dist/tools/builtins/lsp.d.ts.map +1 -0
- package/dist/tools/builtins/lsp.js +102 -0
- package/dist/tools/builtins/lsp.js.map +1 -0
- package/dist/tools/builtins/pty.d.ts +64 -0
- package/dist/tools/builtins/pty.d.ts.map +1 -0
- package/dist/tools/builtins/pty.js +536 -0
- package/dist/tools/builtins/pty.js.map +1 -0
- package/dist/tools/builtins/read.d.ts +3 -0
- package/dist/tools/builtins/read.d.ts.map +1 -0
- package/dist/tools/builtins/read.js +18 -0
- package/dist/tools/builtins/read.js.map +1 -0
- package/dist/tools/builtins/web-fetch.d.ts +4 -0
- package/dist/tools/builtins/web-fetch.d.ts.map +1 -0
- package/dist/tools/builtins/web-fetch.js +353 -0
- package/dist/tools/builtins/web-fetch.js.map +1 -0
- package/dist/tools/builtins/write.d.ts +3 -0
- package/dist/tools/builtins/write.d.ts.map +1 -0
- package/dist/tools/builtins/write.js +48 -0
- package/dist/tools/builtins/write.js.map +1 -0
- package/dist/tools/builtins.d.ts +9 -0
- package/dist/tools/builtins.d.ts.map +1 -0
- package/dist/tools/builtins.js +29 -0
- package/dist/tools/builtins.js.map +1 -0
- package/dist/tools/diff.d.ts +18 -0
- package/dist/tools/diff.d.ts.map +1 -0
- package/dist/tools/diff.js +57 -0
- package/dist/tools/diff.js.map +1 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +9 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/permission.d.ts +120 -0
- package/dist/tools/permission.d.ts.map +1 -0
- package/dist/tools/permission.js +208 -0
- package/dist/tools/permission.js.map +1 -0
- package/dist/tools/registry.d.ts +12 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +19 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/types.d.ts +244 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +15 -0
- package/dist/tools/types.js.map +1 -0
- package/package.json +109 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { PermissionPolicy, ToolCategory } from "@chances-ai/runtime/config";
|
|
2
|
+
import type { ApprovalMode } from "@chances-ai/runtime";
|
|
3
|
+
import type { AuthorizationRequest, PermissionResolver, QuestionDecision, QuestionRequest } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* (5.3) The full result of a permission check. `check()` returns just `allow`
|
|
6
|
+
* for backwards-compatible callers; `evaluate()` returns this so the engine can
|
|
7
|
+
* surface a precise reason (plan-mode block, deny-with-feedback) in the tool
|
|
8
|
+
* result.
|
|
9
|
+
*/
|
|
10
|
+
export interface PermissionEvaluation {
|
|
11
|
+
allow: boolean;
|
|
12
|
+
/** Why it was denied (or mode-allowed); surfaced in the tool result. */
|
|
13
|
+
reason?: string;
|
|
14
|
+
/** Which layer decided. `mode` = the session approval mode forced it. */
|
|
15
|
+
source?: "policy" | "mode" | "resolver";
|
|
16
|
+
/** Deny-with-feedback: free text the user wants relayed to the model. */
|
|
17
|
+
feedback?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Chain of responsibility for a tool invocation:
|
|
21
|
+
* per-tool rule -> category default -> (prompt|host-bridged) -> resolver.
|
|
22
|
+
* `allow` short-circuits true, `deny` short-circuits false, the rest delegate
|
|
23
|
+
* to the resolver (which is the interactive prompt, or an auto-policy in -p mode).
|
|
24
|
+
*
|
|
25
|
+
* **Session positive-decision cache.** Once the resolver returns `true` for a
|
|
26
|
+
* tool name with a `prompt`-category default, subsequent checks of the same
|
|
27
|
+
* tool name skip the resolver for the rest of this `PermissionGate`'s
|
|
28
|
+
* lifetime. This is scoped to MCP-bridged names (`mcp__*`) by default — the
|
|
29
|
+
* `cacheScope` predicate is overridable for tests and future expansion. The
|
|
30
|
+
* cache only stores positives; a `false` from the resolver always re-prompts
|
|
31
|
+
* (so a user who initially says "no" doesn't get silently locked out when
|
|
32
|
+
* they want to reconsider).
|
|
33
|
+
*
|
|
34
|
+
* Rationale: a freshly-added MCP server typically exposes 5–30 tools. Without
|
|
35
|
+
* a cache, the user is re-prompted on every single call for every single
|
|
36
|
+
* tool — claude-code and oh-my-pi both cache, and the trust signal is the
|
|
37
|
+
* user's act of adding the server to config in the first place. Per-tool
|
|
38
|
+
* explicit `deny` in `policy.tools` still wins (we check that first).
|
|
39
|
+
*/
|
|
40
|
+
export type PermissionCacheScope = (req: AuthorizationRequest) => boolean;
|
|
41
|
+
export declare class PermissionGate {
|
|
42
|
+
private readonly policy;
|
|
43
|
+
private readonly resolver;
|
|
44
|
+
private readonly cacheScope;
|
|
45
|
+
private readonly getMode;
|
|
46
|
+
private readonly allowCache;
|
|
47
|
+
/**
|
|
48
|
+
* Promise chain that serialises every resolver invocation across the gate.
|
|
49
|
+
* Without this, two concurrent tool calls (e.g. parent's sync tool +
|
|
50
|
+
* background child's tool, post-3.4) both reach `this.resolver(...)`,
|
|
51
|
+
* and the TUI's view-model has exactly one `pending` slot — the second
|
|
52
|
+
* request overwrites the first, orphaning the first promise. Codex
|
|
53
|
+
* Round-1 MUST-FIX #1.
|
|
54
|
+
*
|
|
55
|
+
* The chain only wraps the actual resolver call: every fast-path
|
|
56
|
+
* decision (allow rule, deny rule, positive-decision cache hit) returns
|
|
57
|
+
* BEFORE acquiring the lock, so non-prompt categories stay O(1) and
|
|
58
|
+
* don't slow down with a queue of in-flight prompts.
|
|
59
|
+
*/
|
|
60
|
+
private resolveLock;
|
|
61
|
+
constructor(policy: PermissionPolicy, resolver: PermissionResolver, cacheScope?: PermissionCacheScope, getMode?: () => ApprovalMode);
|
|
62
|
+
/** Boolean shim over {@link evaluate} for callers that only need the verdict
|
|
63
|
+
* (RPC/ACP host, LSP resolver, legacy tests). */
|
|
64
|
+
check(req: AuthorizationRequest): Promise<boolean>;
|
|
65
|
+
/**
|
|
66
|
+
* (5.3) Whether the session mode blocks this whole tool category outright
|
|
67
|
+
* (plan's read-only floor), as a synchronous pre-check the engine runs
|
|
68
|
+
* BEFORE a tool's `permission()` null-bypass — so plan can't be skipped by an
|
|
69
|
+
* op that would otherwise bypass the gate (codex Round-1 MUST-FIX #1). Pure
|
|
70
|
+
* function of `(category, getMode())`; shares `isBlockedByMode` with
|
|
71
|
+
* `evaluate` so there's one source of truth.
|
|
72
|
+
*/
|
|
73
|
+
blockByMode(category: ToolCategory): {
|
|
74
|
+
reason: string;
|
|
75
|
+
} | null;
|
|
76
|
+
/**
|
|
77
|
+
* Full decision. Precedence (docs/5.3-design.md D3) over the existing static
|
|
78
|
+
* lookup `tools[name] ?? defaults[category]`:
|
|
79
|
+
* 1. static `deny` (per-tool OR category) → DENY — the absolute floor no
|
|
80
|
+
* mode overrides (codex Round-1 MUST-FIX #4: yolo must not bypass a
|
|
81
|
+
* configured deny).
|
|
82
|
+
* 2. mode blocks the tier (plan) → DENY — overrides a standing `allow`.
|
|
83
|
+
* 3. static `allow` → ALLOW.
|
|
84
|
+
* 4. mode auto-approves the tier (yolo / auto-edit) → ALLOW, no prompt.
|
|
85
|
+
* 5. static `prompt`/`host-bridged` → positive-cache hit, else resolver.
|
|
86
|
+
*/
|
|
87
|
+
evaluate(req: AuthorizationRequest): Promise<PermissionEvaluation>;
|
|
88
|
+
/**
|
|
89
|
+
* (5.10b SHOULD-FIX #2) Surgical extraction of the per-gate serialization lock
|
|
90
|
+
* shared by `evaluate` (authorization) and `ask` (question): snapshot the
|
|
91
|
+
* previous lock, install our own, await the snapshot (chaining works whether
|
|
92
|
+
* or not it rejected), run `fn`, release. Nothing evaluate-specific lives here.
|
|
93
|
+
*/
|
|
94
|
+
private withResolverLock;
|
|
95
|
+
/**
|
|
96
|
+
* (5.10b) The QUESTION channel — used ONLY by the read-only `ask_user_question`
|
|
97
|
+
* tool (the engine fail-closes any other tool that emits a `question` request).
|
|
98
|
+
* Deliberately bypasses `evaluate`'s allow / auto-approve / positive-cache /
|
|
99
|
+
* mode precedence — a question is not an authorization — but PRESERVES the
|
|
100
|
+
* static `deny` floor (per-tool rule OR category default) so an operator can
|
|
101
|
+
* disable the asker. Shares `evaluate`'s serialization lock so a question and
|
|
102
|
+
* an authorization never race for the resolver's single `pending` slot. A
|
|
103
|
+
* non-question (or stray boolean) decision ⇒ declined (never auth-on-question).
|
|
104
|
+
*/
|
|
105
|
+
ask(req: QuestionRequest): Promise<QuestionDecision>;
|
|
106
|
+
/**
|
|
107
|
+
* Drops one cache key from the positive-decision cache. The MCP host
|
|
108
|
+
* calls this with the tool name when a server is restarted or
|
|
109
|
+
* removed — without it, a previously approved tool name could silently
|
|
110
|
+
* authorize a *different* implementation after the server's tool set
|
|
111
|
+
* changed. (5.1) Op-enum tools call this with their per-op cache key
|
|
112
|
+
* (e.g. `pty.kill.SIGTERM/<session_id>`) when a session terminates so
|
|
113
|
+
* a re-used session id can't carry a stale approval.
|
|
114
|
+
*/
|
|
115
|
+
invalidate(name: string): void;
|
|
116
|
+
/** Drops every cached positive decision. Useful for "I'm rotating creds,
|
|
117
|
+
* re-prompt me" UX or as a wholesale teardown when MCP is reloaded. */
|
|
118
|
+
invalidateAll(): void;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=permission.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission.d.ts","sourceRoot":"","sources":["../../src/tools/permission.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AACjF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EACV,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EAChB,MAAM,YAAY,CAAC;AAGpB;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,OAAO,CAAC;IACf,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,MAAM,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;IACxC,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,GAAG,EAAE,oBAAoB,KAAK,OAAO,CAAC;AAsB1E,qBAAa,cAAc;IAkBvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAO3B,OAAO,CAAC,QAAQ,CAAC,OAAO;IA1B1B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,WAAW,CAAoC;gBAGpC,MAAM,EAAE,gBAAgB,EACxB,QAAQ,EAAE,kBAAkB,EAC5B,UAAU,GAAE,oBAA0C,EAOtD,OAAO,GAAE,MAAM,YAA8B;IAGhE;sDACkD;IAC5C,KAAK,CAAC,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC;IAIxD;;;;;;;OAOG;IACH,WAAW,CAAC,QAAQ,EAAE,YAAY,GAAG;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAK9D;;;;;;;;;;OAUG;IACG,QAAQ,CAAC,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAkExE;;;;;OAKG;YACW,gBAAgB;IAc9B;;;;;;;;;OASG;IACG,GAAG,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAa1D;;;;;;;;OAQG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAI9B;2EACuE;IACvE,aAAa,IAAI,IAAI;CAGtB"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { isAutoApprovedByMode, isBlockedByMode, modeBlockReason } from "./approval.js";
|
|
2
|
+
/**
|
|
3
|
+
* Default cache scope. Three cohorts are cached by default:
|
|
4
|
+
* 1. **MCP-bridged tools** (`mcp__*`) — pre-5.1 behaviour preserved.
|
|
5
|
+
* The user's act of adding a server to config is the trust
|
|
6
|
+
* signal; once they say "yes" to a tool, they shouldn't be
|
|
7
|
+
* re-prompted on every call.
|
|
8
|
+
* 2. **Per-op cacheKey shapes** — any request that ships an
|
|
9
|
+
* explicit `cacheKey` opts in to caching. The op-enum tools
|
|
10
|
+
* (5.1's `pty`, future siblings) use this for per-`(session,
|
|
11
|
+
* operation, arg-class)` scoping so e.g. approving SIGTERM on
|
|
12
|
+
* session X does not pre-approve SIGKILL on the same session
|
|
13
|
+
* (codex Round-2 MUST-FIX #2 — without this broadening the
|
|
14
|
+
* hook's cacheKey was dead code).
|
|
15
|
+
* 3. Everything else stays un-cached so the user re-prompts per
|
|
16
|
+
* call by default — that matches `web_fetch`'s per-URL gating
|
|
17
|
+
* intent and the file-write defaults.
|
|
18
|
+
*/
|
|
19
|
+
const DEFAULT_CACHE_SCOPE = (req) => req.name.startsWith("mcp__") || req.cacheKey !== undefined;
|
|
20
|
+
export class PermissionGate {
|
|
21
|
+
policy;
|
|
22
|
+
resolver;
|
|
23
|
+
cacheScope;
|
|
24
|
+
getMode;
|
|
25
|
+
allowCache = new Set();
|
|
26
|
+
/**
|
|
27
|
+
* Promise chain that serialises every resolver invocation across the gate.
|
|
28
|
+
* Without this, two concurrent tool calls (e.g. parent's sync tool +
|
|
29
|
+
* background child's tool, post-3.4) both reach `this.resolver(...)`,
|
|
30
|
+
* and the TUI's view-model has exactly one `pending` slot — the second
|
|
31
|
+
* request overwrites the first, orphaning the first promise. Codex
|
|
32
|
+
* Round-1 MUST-FIX #1.
|
|
33
|
+
*
|
|
34
|
+
* The chain only wraps the actual resolver call: every fast-path
|
|
35
|
+
* decision (allow rule, deny rule, positive-decision cache hit) returns
|
|
36
|
+
* BEFORE acquiring the lock, so non-prompt categories stay O(1) and
|
|
37
|
+
* don't slow down with a queue of in-flight prompts.
|
|
38
|
+
*/
|
|
39
|
+
resolveLock = Promise.resolve();
|
|
40
|
+
constructor(policy, resolver, cacheScope = DEFAULT_CACHE_SCOPE,
|
|
41
|
+
// (5.3) Session approval-mode getter. Optional 4th positional so every
|
|
42
|
+
// existing `new PermissionGate(policy, resolver[, cacheScope])` call site
|
|
43
|
+
// keeps working at `default` mode (codex Round-1 MUST-FIX #2 — back-compat).
|
|
44
|
+
// A getter (not the holder) keeps tools decoupled from runtime's
|
|
45
|
+
// ApprovalState; read fresh on every check so a mid-turn Shift+Tab lands
|
|
46
|
+
// immediately.
|
|
47
|
+
getMode = () => "default") {
|
|
48
|
+
this.policy = policy;
|
|
49
|
+
this.resolver = resolver;
|
|
50
|
+
this.cacheScope = cacheScope;
|
|
51
|
+
this.getMode = getMode;
|
|
52
|
+
}
|
|
53
|
+
/** Boolean shim over {@link evaluate} for callers that only need the verdict
|
|
54
|
+
* (RPC/ACP host, LSP resolver, legacy tests). */
|
|
55
|
+
async check(req) {
|
|
56
|
+
return (await this.evaluate(req)).allow;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* (5.3) Whether the session mode blocks this whole tool category outright
|
|
60
|
+
* (plan's read-only floor), as a synchronous pre-check the engine runs
|
|
61
|
+
* BEFORE a tool's `permission()` null-bypass — so plan can't be skipped by an
|
|
62
|
+
* op that would otherwise bypass the gate (codex Round-1 MUST-FIX #1). Pure
|
|
63
|
+
* function of `(category, getMode())`; shares `isBlockedByMode` with
|
|
64
|
+
* `evaluate` so there's one source of truth.
|
|
65
|
+
*/
|
|
66
|
+
blockByMode(category) {
|
|
67
|
+
const mode = this.getMode();
|
|
68
|
+
return isBlockedByMode(category, mode) ? { reason: modeBlockReason(mode) } : null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Full decision. Precedence (docs/5.3-design.md D3) over the existing static
|
|
72
|
+
* lookup `tools[name] ?? defaults[category]`:
|
|
73
|
+
* 1. static `deny` (per-tool OR category) → DENY — the absolute floor no
|
|
74
|
+
* mode overrides (codex Round-1 MUST-FIX #4: yolo must not bypass a
|
|
75
|
+
* configured deny).
|
|
76
|
+
* 2. mode blocks the tier (plan) → DENY — overrides a standing `allow`.
|
|
77
|
+
* 3. static `allow` → ALLOW.
|
|
78
|
+
* 4. mode auto-approves the tier (yolo / auto-edit) → ALLOW, no prompt.
|
|
79
|
+
* 5. static `prompt`/`host-bridged` → positive-cache hit, else resolver.
|
|
80
|
+
*/
|
|
81
|
+
async evaluate(req) {
|
|
82
|
+
// (5.1) Cache key falls back to `name`. Per-op shapes set `cacheKey`.
|
|
83
|
+
const key = req.cacheKey ?? req.name;
|
|
84
|
+
const mode = this.getMode();
|
|
85
|
+
const staticDecision = this.policy.tools?.[req.name] ?? this.policy.defaults[req.category];
|
|
86
|
+
if (staticDecision === "deny") {
|
|
87
|
+
return { allow: false, reason: `denied by policy (${req.category})`, source: "policy" };
|
|
88
|
+
}
|
|
89
|
+
if (isBlockedByMode(req.category, mode)) {
|
|
90
|
+
return { allow: false, reason: modeBlockReason(mode), source: "mode" };
|
|
91
|
+
}
|
|
92
|
+
if (staticDecision === "allow")
|
|
93
|
+
return { allow: true, source: "policy" };
|
|
94
|
+
if (isAutoApprovedByMode(req.category, mode))
|
|
95
|
+
return { allow: true, source: "mode" };
|
|
96
|
+
// "prompt" / "host-bridged" defer to the resolver unless a positive
|
|
97
|
+
// decision for this key is already cached this session. The cache only
|
|
98
|
+
// holds keys that were intentionally remembered (scoped auto-cache OR an
|
|
99
|
+
// explicit `remember:true` "always"), so a hit is always honored —
|
|
100
|
+
// independent of `cacheScope`, which only governs *adding* below.
|
|
101
|
+
if (this.allowCache.has(key))
|
|
102
|
+
return { allow: true, source: "policy" };
|
|
103
|
+
// Acquire the per-gate serialisation lock, then run the resolver step. The
|
|
104
|
+
// post-wait cache re-check + mode re-read live INSIDE this callback (NOT in
|
|
105
|
+
// `withResolverLock`) — they are evaluate-specific, not generic to the lock
|
|
106
|
+
// (codex R1 SHOULD-FIX #2 — surgical extraction).
|
|
107
|
+
return this.withResolverLock(async () => {
|
|
108
|
+
// Re-check the cache after the wait — another queued call may have
|
|
109
|
+
// populated it (see the read-before-lock note above re: cacheScope).
|
|
110
|
+
if (this.allowCache.has(key))
|
|
111
|
+
return { allow: true, source: "policy" };
|
|
112
|
+
// (5.3 codex Round-1 SHOULD-FIX #1) Re-read the mode after the wait too:
|
|
113
|
+
// the user may have flipped to plan (→ block) or yolo (→ skip the now-
|
|
114
|
+
// stale prompt) while this request sat in the queue.
|
|
115
|
+
const modeNow = this.getMode();
|
|
116
|
+
if (isBlockedByMode(req.category, modeNow)) {
|
|
117
|
+
return { allow: false, reason: modeBlockReason(modeNow), source: "mode" };
|
|
118
|
+
}
|
|
119
|
+
if (isAutoApprovedByMode(req.category, modeNow))
|
|
120
|
+
return { allow: true, source: "mode" };
|
|
121
|
+
// (5.2 MUST-FIX #1 / 5.3) The resolver returns a bare boolean (legacy) or
|
|
122
|
+
// a PermissionDecision. Caching rule:
|
|
123
|
+
// - bare boolean: cache iff `cacheScope` (legacy, scope-gated).
|
|
124
|
+
// - {remember:false}: never cache (allow-once).
|
|
125
|
+
// - {remember:true}: cache regardless of scope — an explicit human/host
|
|
126
|
+
// "always" should stick even for un-scoped tools like file-write
|
|
127
|
+
// (this is the TUI's "[a] yes, don't ask" path).
|
|
128
|
+
// - {} (remember omitted): scope-gated, preserving 5.2 ACP semantics.
|
|
129
|
+
const decision = await this.resolver(req);
|
|
130
|
+
// (5.10b) A question decision must never reach the authorization path — it
|
|
131
|
+
// carries no allow/deny. Defensive: the engine only sends authorization
|
|
132
|
+
// requests here, so a stray question ⇒ treat as a denial.
|
|
133
|
+
if (typeof decision !== "boolean" && decision.kind === "question") {
|
|
134
|
+
return { allow: false, source: "resolver" };
|
|
135
|
+
}
|
|
136
|
+
const explicit = typeof decision !== "boolean";
|
|
137
|
+
const granted = explicit ? decision.allow : decision;
|
|
138
|
+
const remember = explicit ? decision.remember !== false : true;
|
|
139
|
+
const feedback = explicit ? decision.feedback : undefined;
|
|
140
|
+
const cacheIt = granted && remember && (this.cacheScope(req) || (explicit && decision.remember === true));
|
|
141
|
+
if (cacheIt)
|
|
142
|
+
this.allowCache.add(key);
|
|
143
|
+
return granted
|
|
144
|
+
? { allow: true, source: "resolver" }
|
|
145
|
+
: { allow: false, source: "resolver", feedback };
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* (5.10b SHOULD-FIX #2) Surgical extraction of the per-gate serialization lock
|
|
150
|
+
* shared by `evaluate` (authorization) and `ask` (question): snapshot the
|
|
151
|
+
* previous lock, install our own, await the snapshot (chaining works whether
|
|
152
|
+
* or not it rejected), run `fn`, release. Nothing evaluate-specific lives here.
|
|
153
|
+
*/
|
|
154
|
+
async withResolverLock(fn) {
|
|
155
|
+
const previousLock = this.resolveLock;
|
|
156
|
+
let release;
|
|
157
|
+
this.resolveLock = new Promise((r) => {
|
|
158
|
+
release = r;
|
|
159
|
+
});
|
|
160
|
+
try {
|
|
161
|
+
await previousLock.catch(() => { });
|
|
162
|
+
return await fn();
|
|
163
|
+
}
|
|
164
|
+
finally {
|
|
165
|
+
release();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* (5.10b) The QUESTION channel — used ONLY by the read-only `ask_user_question`
|
|
170
|
+
* tool (the engine fail-closes any other tool that emits a `question` request).
|
|
171
|
+
* Deliberately bypasses `evaluate`'s allow / auto-approve / positive-cache /
|
|
172
|
+
* mode precedence — a question is not an authorization — but PRESERVES the
|
|
173
|
+
* static `deny` floor (per-tool rule OR category default) so an operator can
|
|
174
|
+
* disable the asker. Shares `evaluate`'s serialization lock so a question and
|
|
175
|
+
* an authorization never race for the resolver's single `pending` slot. A
|
|
176
|
+
* non-question (or stray boolean) decision ⇒ declined (never auth-on-question).
|
|
177
|
+
*/
|
|
178
|
+
async ask(req) {
|
|
179
|
+
const staticDecision = this.policy.tools?.[req.name] ?? this.policy.defaults[req.category];
|
|
180
|
+
if (staticDecision === "deny") {
|
|
181
|
+
return { kind: "question", answers: {}, declined: true };
|
|
182
|
+
}
|
|
183
|
+
return this.withResolverLock(async () => {
|
|
184
|
+
const decision = await this.resolver(req);
|
|
185
|
+
return typeof decision === "boolean" || decision.kind !== "question"
|
|
186
|
+
? { kind: "question", answers: {}, declined: true }
|
|
187
|
+
: decision;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Drops one cache key from the positive-decision cache. The MCP host
|
|
192
|
+
* calls this with the tool name when a server is restarted or
|
|
193
|
+
* removed — without it, a previously approved tool name could silently
|
|
194
|
+
* authorize a *different* implementation after the server's tool set
|
|
195
|
+
* changed. (5.1) Op-enum tools call this with their per-op cache key
|
|
196
|
+
* (e.g. `pty.kill.SIGTERM/<session_id>`) when a session terminates so
|
|
197
|
+
* a re-used session id can't carry a stale approval.
|
|
198
|
+
*/
|
|
199
|
+
invalidate(name) {
|
|
200
|
+
this.allowCache.delete(name);
|
|
201
|
+
}
|
|
202
|
+
/** Drops every cached positive decision. Useful for "I'm rotating creds,
|
|
203
|
+
* re-prompt me" UX or as a wholesale teardown when MCP is reloaded. */
|
|
204
|
+
invalidateAll() {
|
|
205
|
+
this.allowCache.clear();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=permission.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission.js","sourceRoot":"","sources":["../../src/tools/permission.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAyCvF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,mBAAmB,GAAyB,CAAC,GAAG,EAAE,EAAE,CACxD,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC;AAE7D,MAAM,OAAO,cAAc;IAkBN;IACA;IACA;IAOA;IA1BF,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IAChD;;;;;;;;;;;;OAYG;IACK,WAAW,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IAEvD,YACmB,MAAwB,EACxB,QAA4B,EAC5B,aAAmC,mBAAmB;IACvE,uEAAuE;IACvE,0EAA0E;IAC1E,6EAA6E;IAC7E,iEAAiE;IACjE,yEAAyE;IACzE,eAAe;IACE,UAA8B,GAAG,EAAE,CAAC,SAAS;QAT7C,WAAM,GAAN,MAAM,CAAkB;QACxB,aAAQ,GAAR,QAAQ,CAAoB;QAC5B,eAAU,GAAV,UAAU,CAA4C;QAOtD,YAAO,GAAP,OAAO,CAAsC;IAC7D,CAAC;IAEJ;sDACkD;IAClD,KAAK,CAAC,KAAK,CAAC,GAAyB;QACnC,OAAO,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;IAC1C,CAAC;IAED;;;;;;;OAOG;IACH,WAAW,CAAC,QAAsB;QAChC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAC5B,OAAO,eAAe,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACpF,CAAC;IAED;;;;;;;;;;OAUG;IACH,KAAK,CAAC,QAAQ,CAAC,GAAyB;QACtC,sEAAsE;QACtE,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI,CAAC;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAC5B,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAE3F,IAAI,cAAc,KAAK,MAAM,EAAE,CAAC;YAC9B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,qBAAqB,GAAG,CAAC,QAAQ,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;QAC1F,CAAC;QACD,IAAI,eAAe,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC;YACxC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QACzE,CAAC;QACD,IAAI,cAAc,KAAK,OAAO;YAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;QACzE,IAAI,oBAAoB,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAErF,oEAAoE;QACpE,uEAAuE;QACvE,yEAAyE;QACzE,mEAAmE;QACnE,kEAAkE;QAClE,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;QAEvE,2EAA2E;QAC3E,4EAA4E;QAC5E,4EAA4E;QAC5E,kDAAkD;QAClD,OAAO,IAAI,CAAC,gBAAgB,CAAC,KAAK,IAAI,EAAE;YACtC,mEAAmE;YACnE,qEAAqE;YACrE,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;YACvE,yEAAyE;YACzE,uEAAuE;YACvE,qDAAqD;YACrD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;YAC/B,IAAI,eAAe,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC;gBAC3C,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;YAC5E,CAAC;YACD,IAAI,oBAAoB,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC;gBAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;YAExF,0EAA0E;YAC1E,sCAAsC;YACtC,kEAAkE;YAClE,kDAAkD;YAClD,0EAA0E;YAC1E,qEAAqE;YACrE,qDAAqD;YACrD,wEAAwE;YACxE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC1C,2EAA2E;YAC3E,wEAAwE;YACxE,0DAA0D;YAC1D,IAAI,OAAO,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBAClE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;YAC9C,CAAC;YACD,MAAM,QAAQ,GAAG,OAAO,QAAQ,KAAK,SAAS,CAAC;YAC/C,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;YACrD,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;YAC/D,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;YAC1D,MAAM,OAAO,GAAG,OAAO,IAAI,QAAQ,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC;YAC1G,IAAI,OAAO;gBAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACtC,OAAO,OAAO;gBACZ,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE;gBACrC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;QACrD,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,gBAAgB,CAAI,EAAoB;QACpD,MAAM,YAAY,GAAG,IAAI,CAAC,WAAW,CAAC;QACtC,IAAI,OAAoB,CAAC;QACzB,IAAI,CAAC,WAAW,GAAG,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE;YACzC,OAAO,GAAG,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACnC,OAAO,MAAM,EAAE,EAAE,CAAC;QACpB,CAAC;gBAAS,CAAC;YACT,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,GAAG,CAAC,GAAoB;QAC5B,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC3F,IAAI,cAAc,KAAK,MAAM,EAAE,CAAC;YAC9B,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC3D,CAAC;QACD,OAAO,IAAI,CAAC,gBAAgB,CAAC,KAAK,IAAI,EAAE;YACtC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC1C,OAAO,OAAO,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,IAAI,KAAK,UAAU;gBAClE,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBACnD,CAAC,CAAC,QAAQ,CAAC;QACf,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;OAQG;IACH,UAAU,CAAC,IAAY;QACrB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED;2EACuE;IACvE,aAAa;QACX,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC;CACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Tool } from "./types.js";
|
|
2
|
+
/** Registry pattern: built-in and plugin tools share one namespace. */
|
|
3
|
+
export declare class ToolRegistry {
|
|
4
|
+
private readonly tools;
|
|
5
|
+
register(tool: Tool): void;
|
|
6
|
+
/** Removes a tool by name, returning true if it existed. Used by PluginHost
|
|
7
|
+
* rollback when a plugin's onLoad throws after registering a tool. */
|
|
8
|
+
unregister(name: string): boolean;
|
|
9
|
+
get(name: string): Tool | undefined;
|
|
10
|
+
list(): Tool[];
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/tools/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,uEAAuE;AACvE,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2B;IAEjD,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAI1B;0EACsE;IACtE,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIjC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS;IAInC,IAAI,IAAI,IAAI,EAAE;CAGf"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Registry pattern: built-in and plugin tools share one namespace. */
|
|
2
|
+
export class ToolRegistry {
|
|
3
|
+
tools = new Map();
|
|
4
|
+
register(tool) {
|
|
5
|
+
this.tools.set(tool.name, tool);
|
|
6
|
+
}
|
|
7
|
+
/** Removes a tool by name, returning true if it existed. Used by PluginHost
|
|
8
|
+
* rollback when a plugin's onLoad throws after registering a tool. */
|
|
9
|
+
unregister(name) {
|
|
10
|
+
return this.tools.delete(name);
|
|
11
|
+
}
|
|
12
|
+
get(name) {
|
|
13
|
+
return this.tools.get(name);
|
|
14
|
+
}
|
|
15
|
+
list() {
|
|
16
|
+
return [...this.tools.values()];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/tools/registry.ts"],"names":[],"mappings":"AAEA,uEAAuE;AACvE,MAAM,OAAO,YAAY;IACN,KAAK,GAAG,IAAI,GAAG,EAAgB,CAAC;IAEjD,QAAQ,CAAC,IAAU;QACjB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAED;0EACsE;IACtE,UAAU,CAAC,IAAY;QACrB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,IAAI;QACF,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IAClC,CAAC;CACF"}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import type { JSONValue } from "@chances-ai/runtime";
|
|
2
|
+
import type { ToolCategory } from "@chances-ai/runtime/config";
|
|
3
|
+
export type { ToolCategory };
|
|
4
|
+
/** Minimum LSP host shape consumed by the `lsp` built-in tool. Kept
|
|
5
|
+
* narrow so this package doesn't depend on `@chances-ai/lsp` for
|
|
6
|
+
* types — the engine wires the real instance in at boot time and
|
|
7
|
+
* downcasts. (codex Round-1 MUST-FIX #1.) */
|
|
8
|
+
export interface ToolLspHost {
|
|
9
|
+
execute(req: {
|
|
10
|
+
op: string;
|
|
11
|
+
filePath?: string;
|
|
12
|
+
line?: number;
|
|
13
|
+
character?: number;
|
|
14
|
+
query?: string;
|
|
15
|
+
cwd: string;
|
|
16
|
+
signal: AbortSignal;
|
|
17
|
+
}): Promise<{
|
|
18
|
+
ok: boolean;
|
|
19
|
+
output: string;
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
export interface ToolContext {
|
|
23
|
+
workspaceRoot: string;
|
|
24
|
+
cwd: string;
|
|
25
|
+
signal: AbortSignal;
|
|
26
|
+
/**
|
|
27
|
+
* (4.1) The parent (non-isolated) workspace root. Inside an isolated
|
|
28
|
+
* subagent, `workspaceRoot` and `cwd` flip to the worktree path, but
|
|
29
|
+
* **artifact persistence paths** (e.g. `bash`'s oversized-output dump
|
|
30
|
+
* under `.chances/tool-results/`) should land in the PARENT workspace
|
|
31
|
+
* so the parent's `.chances/` directory accumulates the durable
|
|
32
|
+
* record. Otherwise a clean-removed worktree could swallow the
|
|
33
|
+
* persisted file just as the tool returns its path.
|
|
34
|
+
*
|
|
35
|
+
* Outside isolation, this equals `workspaceRoot`. Tools that emit
|
|
36
|
+
* artifacts should prefer `parentWorkspaceRoot ?? workspaceRoot` so
|
|
37
|
+
* the contract is uniform across both branches. Round-2 SHOULD-FIX
|
|
38
|
+
* #4.
|
|
39
|
+
*/
|
|
40
|
+
parentWorkspaceRoot?: string;
|
|
41
|
+
/**
|
|
42
|
+
* (4.2) Optional LSP host wired in at boot when the user has
|
|
43
|
+
* `vscode-jsonrpc` installed AND `config.lsp.enabled !== false`.
|
|
44
|
+
* Undefined when the peer is absent or LSP is disabled — the `lsp`
|
|
45
|
+
* tool fails fast with a labelled `ok:false` in that case.
|
|
46
|
+
* codex Round-1 MUST-FIX #1 — wrapper cannot reach a CLI-boot-time
|
|
47
|
+
* host without this engine plumbing.
|
|
48
|
+
*/
|
|
49
|
+
lsp?: ToolLspHost;
|
|
50
|
+
/**
|
|
51
|
+
* (5.7) The engine's tool-call id for THIS execution — the same
|
|
52
|
+
* `callId` carried on the `tool:call` / `tool:result` bus events and
|
|
53
|
+
* the provider's `tool_call.id`. The engine stamps it on the ctx
|
|
54
|
+
* before both `permission()` and `execute()` (see `core/engine.ts`).
|
|
55
|
+
*
|
|
56
|
+
* Tools that persist oversized output use it to name the artifact
|
|
57
|
+
* directory (`.chances/tool-results/<callId>/…`) so a client can join
|
|
58
|
+
* the saved file to the `tool_result` it received — mirrors
|
|
59
|
+
* claude-code's `.claude/tool-results/<toolUseId>`. Undefined for
|
|
60
|
+
* non-engine callers (tests, plugins invoking a tool directly), where
|
|
61
|
+
* persistence falls back to a freshly-minted `createId`.
|
|
62
|
+
*/
|
|
63
|
+
callId?: string;
|
|
64
|
+
/**
|
|
65
|
+
* (5.10b) The user's answers to an `ask_user_question` call. The engine
|
|
66
|
+
* populates this ONLY on the question branch — and ONLY for the read-only ask
|
|
67
|
+
* tool — immediately before `execute()`; `summarize()` and every other
|
|
68
|
+
* tool/branch never see it. The ask tool reads it to format the model-facing
|
|
69
|
+
* result string (the engine never serializes answers itself).
|
|
70
|
+
*/
|
|
71
|
+
questionDecision?: QuestionDecision;
|
|
72
|
+
}
|
|
73
|
+
export interface ToolResult {
|
|
74
|
+
ok: boolean;
|
|
75
|
+
output: string;
|
|
76
|
+
}
|
|
77
|
+
/** Command pattern: each tool is a self-describing, executable unit. */
|
|
78
|
+
export interface Tool {
|
|
79
|
+
name: string;
|
|
80
|
+
description: string;
|
|
81
|
+
category: ToolCategory;
|
|
82
|
+
/** JSON Schema for the args object (shared with the provider via core). */
|
|
83
|
+
parameters: JSONValue;
|
|
84
|
+
/** One-line, human-readable description of a specific invocation (used by the
|
|
85
|
+
* permission prompt and diff approval). Receives the same ctx as execute so
|
|
86
|
+
* that path-aware previews (e.g. create vs overwrite) resolve against the
|
|
87
|
+
* workspace, not process.cwd. */
|
|
88
|
+
summarize(args: JSONValue, ctx: ToolContext): string;
|
|
89
|
+
execute(args: JSONValue, ctx: ToolContext): Promise<ToolResult>;
|
|
90
|
+
/**
|
|
91
|
+
* (5.1) Per-call permission shape. When implemented, the engine asks
|
|
92
|
+
* this hook BEFORE calling `gate.check(...)`:
|
|
93
|
+
* - Return `null` to bypass the gate entirely. Used by op-enum
|
|
94
|
+
* tools (e.g. `pty.read`, `pty.resize`) whose ops are already
|
|
95
|
+
* covered by a parent op's approval.
|
|
96
|
+
* - Return a {@link PermissionRequest} (with optional `cacheKey`)
|
|
97
|
+
* to override the legacy `{name, category, summarize(args)}`
|
|
98
|
+
* shape — lets ops vary their category, prompt summary, and
|
|
99
|
+
* positive-decision cache scope independently.
|
|
100
|
+
*
|
|
101
|
+
* When undefined (the default for all 10 existing builtins), the
|
|
102
|
+
* engine constructs a request from `{name, category, summarize(args),
|
|
103
|
+
* args}` exactly as in 1.x→5.0 — fully backwards compatible.
|
|
104
|
+
*
|
|
105
|
+
* Codex Round-1 MUST-FIX #1 (5.1 design) — without this hook a
|
|
106
|
+
* single op-enum tool can't bypass cheap ops while re-prompting
|
|
107
|
+
* destructive ones.
|
|
108
|
+
*/
|
|
109
|
+
permission?(args: JSONValue, ctx: ToolContext): PermissionRequest | null;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* (5.10b) A request to AUTHORIZE a side-effecting tool call — the original
|
|
113
|
+
* (1.x→5.x) permission shape. `kind` is optional and defaults to
|
|
114
|
+
* `"authorization"`, so every existing producer/resolver that omits it stays
|
|
115
|
+
* byte-identical at runtime. The `question` variant ({@link QuestionRequest})
|
|
116
|
+
* rides the SAME resolver channel but is NOT an authorization — see
|
|
117
|
+
* {@link PermissionGate.ask}.
|
|
118
|
+
*/
|
|
119
|
+
export interface AuthorizationRequest {
|
|
120
|
+
kind?: "authorization";
|
|
121
|
+
name: string;
|
|
122
|
+
category: ToolCategory;
|
|
123
|
+
summary: string;
|
|
124
|
+
args: JSONValue;
|
|
125
|
+
/**
|
|
126
|
+
* (5.1) Optional cache key for `PermissionGate`'s positive-decision
|
|
127
|
+
* cache. When undefined, the gate uses `name` (legacy behaviour). Use
|
|
128
|
+
* this to scope per-call cache decisions (e.g.
|
|
129
|
+
* `pty.kill.SIGTERM/<session_id>` so approving SIGTERM on session
|
|
130
|
+
* `pty_abc` doesn't pre-approve SIGTERM on session `pty_xyz`, and
|
|
131
|
+
* doesn't pre-approve SIGKILL on either). Distinct cache keys are
|
|
132
|
+
* also distinct invalidation targets via `gate.invalidate(cacheKey)`.
|
|
133
|
+
*/
|
|
134
|
+
cacheKey?: string;
|
|
135
|
+
/**
|
|
136
|
+
* (5.2 codex Round-1 MUST-FIX #3) Optional id of the originating tool
|
|
137
|
+
* call. The engine stamps `call.callId` here before `gate.check`, so a
|
|
138
|
+
* resolver that surfaces the prompt to an out-of-process client (the RPC
|
|
139
|
+
* / ACP host) can ship a self-contained `request_permission` frame that
|
|
140
|
+
* the client joins to the preceding `tool_call.callId`. Undefined on
|
|
141
|
+
* direct/in-process resolvers (TUI, auto-policy) that don't need it.
|
|
142
|
+
*/
|
|
143
|
+
callId?: string;
|
|
144
|
+
}
|
|
145
|
+
/** One option in a {@link QuestionSpec}. Schema-locked: the model declares these
|
|
146
|
+
* options; the UI injects the "Other" free-text row itself (the model never
|
|
147
|
+
* declares it). */
|
|
148
|
+
export interface QuestionOption {
|
|
149
|
+
label: string;
|
|
150
|
+
description?: string;
|
|
151
|
+
/** Optional Markdown preview shown side-by-side when present (single-select). */
|
|
152
|
+
preview?: string;
|
|
153
|
+
}
|
|
154
|
+
/** One question in a {@link QuestionRequest}. Mirrors the `ask_user_question`
|
|
155
|
+
* tool's zod schema, which validates it (1–4 questions, 2–4 options, unique
|
|
156
|
+
* labels) BEFORE the request is built. */
|
|
157
|
+
export interface QuestionSpec {
|
|
158
|
+
question: string;
|
|
159
|
+
header: string;
|
|
160
|
+
options: QuestionOption[];
|
|
161
|
+
multiSelect?: boolean;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* (5.10b) A request to ASK the user structured questions — emitted ONLY by the
|
|
165
|
+
* read-only `ask_user_question` tool's `permission()` hook. It deliberately
|
|
166
|
+
* bypasses the allow/deny precedence in {@link PermissionGate.evaluate} (a
|
|
167
|
+
* question is not an authorization) but still honors a static `deny` floor via
|
|
168
|
+
* {@link PermissionGate.ask}. The engine FAIL-CLOSES any OTHER tool that emits
|
|
169
|
+
* this variant (it would otherwise be an authorization escape — codex R1).
|
|
170
|
+
*/
|
|
171
|
+
export interface QuestionRequest {
|
|
172
|
+
kind: "question";
|
|
173
|
+
name: string;
|
|
174
|
+
/** Read-only category of the asker (`search`). `gate.ask` reads it for the
|
|
175
|
+
* static-deny floor; the engine asserts it is a read-only category. */
|
|
176
|
+
category: ToolCategory;
|
|
177
|
+
callId?: string;
|
|
178
|
+
questions: QuestionSpec[];
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* (5.10b) Discriminated union carried by the resolver channel. An omitted
|
|
182
|
+
* `kind` ⇒ authorization (back-compat: every existing producer builds the
|
|
183
|
+
* authorization shape untouched).
|
|
184
|
+
*/
|
|
185
|
+
export type PermissionRequest = AuthorizationRequest | QuestionRequest;
|
|
186
|
+
/**
|
|
187
|
+
* (5.2 codex Round-1 MUST-FIX #1) A resolver decision richer than a bare
|
|
188
|
+
* boolean. `remember: false` tells the gate NOT to cache a positive
|
|
189
|
+
* decision (ACP `allow_once` vs `allow_always`) even when the request is
|
|
190
|
+
* cache-scoped (`mcp__*` / `cacheKey`). Omitted ⇒ `true` ⇒ the gate's
|
|
191
|
+
* pre-5.2 caching behaviour is preserved. `kind` omitted ⇒ authorization
|
|
192
|
+
* (a bare boolean is also an authorization allow/deny).
|
|
193
|
+
*/
|
|
194
|
+
export interface AuthorizationDecision {
|
|
195
|
+
kind?: "authorization";
|
|
196
|
+
allow: boolean;
|
|
197
|
+
remember?: boolean;
|
|
198
|
+
/**
|
|
199
|
+
* (5.3) On a denial (`allow: false`), optional free text the user typed via
|
|
200
|
+
* the "no, and tell the model what to do" prompt option. The gate passes it
|
|
201
|
+
* through to `PermissionEvaluation.feedback`; the engine appends it to the
|
|
202
|
+
* `ok:false` tool result so the model can adapt instead of just retrying.
|
|
203
|
+
* Ignored on an allow. Undefined for the plain "no".
|
|
204
|
+
*/
|
|
205
|
+
feedback?: string;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* (5.10b) The user's answers to a {@link QuestionRequest}, carried back through
|
|
209
|
+
* the resolver. `answers` maps each question text to the selected option labels
|
|
210
|
+
* (plus any free text typed into the "Other" row). `declined` is set when the
|
|
211
|
+
* user cancels OR the resolver is non-interactive (`-p` / RPC / ACP) — the ask
|
|
212
|
+
* tool turns either into a deterministic "declined" tool result.
|
|
213
|
+
*/
|
|
214
|
+
export interface QuestionDecision {
|
|
215
|
+
kind: "question";
|
|
216
|
+
answers: Record<string, string[]>;
|
|
217
|
+
annotations?: Record<string, {
|
|
218
|
+
preview?: string;
|
|
219
|
+
notes?: string;
|
|
220
|
+
}>;
|
|
221
|
+
declined?: boolean;
|
|
222
|
+
}
|
|
223
|
+
/** Discriminated union of resolver decisions. Omitted `kind` ⇒ authorization. */
|
|
224
|
+
export type PermissionDecision = AuthorizationDecision | QuestionDecision;
|
|
225
|
+
/**
|
|
226
|
+
* Asks an external decider (interactive prompt, or auto-policy) to approve a
|
|
227
|
+
* tool call OR answer questions. For an {@link AuthorizationRequest} the
|
|
228
|
+
* resolver returns a bare `boolean` (legacy, = `{allow, remember:true}`) or an
|
|
229
|
+
* {@link AuthorizationDecision}; for a {@link QuestionRequest} it returns a
|
|
230
|
+
* {@link QuestionDecision} (a non-interactive resolver returns
|
|
231
|
+
* `{kind:"question", answers:{}, declined:true}`). Resolvers MUST narrow on
|
|
232
|
+
* `req.kind` before reading authorization-only fields (codex R1 SHOULD-FIX).
|
|
233
|
+
*/
|
|
234
|
+
export type PermissionResolver = (req: PermissionRequest) => Promise<boolean | PermissionDecision>;
|
|
235
|
+
/** The one tool name allowed to use the `question` permission channel. The
|
|
236
|
+
* engine fail-closes any other tool that emits a `question` request (codex R1
|
|
237
|
+
* MUST-FIX: the channel skips `gate.evaluate`, so it must be unreachable by a
|
|
238
|
+
* side-effecting tool). */
|
|
239
|
+
export declare const ASK_USER_QUESTION_TOOL_NAME = "ask_user_question";
|
|
240
|
+
/** Read-only tool categories. The question channel is double-guarded: the asker
|
|
241
|
+
* must BOTH be {@link ASK_USER_QUESTION_TOOL_NAME} AND declare a category in
|
|
242
|
+
* this set, so a side-effecting category can never reach it. */
|
|
243
|
+
export declare const READONLY_CATEGORIES: ReadonlySet<ToolCategory>;
|
|
244
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/tools/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** The one tool name allowed to use the `question` permission channel. The
|
|
2
|
+
* engine fail-closes any other tool that emits a `question` request (codex R1
|
|
3
|
+
* MUST-FIX: the channel skips `gate.evaluate`, so it must be unreachable by a
|
|
4
|
+
* side-effecting tool). */
|
|
5
|
+
export const ASK_USER_QUESTION_TOOL_NAME = "ask_user_question";
|
|
6
|
+
/** Read-only tool categories. The question channel is double-guarded: the asker
|
|
7
|
+
* must BOTH be {@link ASK_USER_QUESTION_TOOL_NAME} AND declare a category in
|
|
8
|
+
* this set, so a side-effecting category can never reach it. */
|
|
9
|
+
export const READONLY_CATEGORIES = new Set([
|
|
10
|
+
"file-read",
|
|
11
|
+
"search",
|
|
12
|
+
"memory-read",
|
|
13
|
+
"network-read",
|
|
14
|
+
]);
|
|
15
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/tools/types.ts"],"names":[],"mappings":"AAqPA;;;4BAG4B;AAC5B,MAAM,CAAC,MAAM,2BAA2B,GAAG,mBAAmB,CAAC;AAE/D;;iEAEiE;AACjE,MAAM,CAAC,MAAM,mBAAmB,GAA8B,IAAI,GAAG,CAAe;IAClF,WAAW;IACX,QAAQ;IACR,aAAa;IACb,cAAc;CACf,CAAC,CAAC"}
|