Make the policy decision point pluggable (the rules are injectable, the evaluator is not)
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
- 难度
- 5/5
- 预计耗时
- 一周以上
- 新手友好度
- 35/100
- Issue 类型
- 功能
- 描述清晰度
- 基本清楚
- 活跃度
- 活跃
- 技术栈
- typescript
调研方向
从 server/src/computer/gateway.ts 的469-535行附近,以及 server/src/computer/policy.ts 的4-12、38-42、44-153和210-243行附近开始。确认 enterprise gateway 是否已经存在可插拔的决策点,然后定义并记录 decider contract,同时保留 snapshot resolution、dry-run 处理、audit 顺序和 fail-closed 行为;完成标准包括默认路径和一个经过完整演示的示例 decider。
由索引模型根据 Issue 内容生成。
描述
What I see today
The policy data is already fully injectable. options.policy() is a getter
(server/src/computer/gateway.ts:523), backed by createPolicyStore with
ACTION_POLICY_TOPIC for live reload (server/src/computer/policy-store.ts).
The evaluator is not. evaluateActionPolicy is a direct import from ./policy,
called synchronously:
const decision = evaluateActionPolicy(options.policy(), context);
So a deployment can change the rules, but not who answers.
Why that's worth a seam
Three cases the current shape can't serve:
-
A deployment that already owns a policy engine. OPA/Rego, Cedar, an internal
service. Today they have to transcribe their rules into CEL and keep two sources in
sync, which is exactly the setup where the two drift and nobody notices. -
Decisions that need state the gateway doesn't hold. "This bot has already
submitted three orders today." "This action is above the amount this actor may
approve alone." "A human granted this, twenty minutes ago, and the grant expires."
CEL over a statelessPolicyContextcan't express any of those — not a flaw in the
expression language, just a different question. -
Decisions that take time. Anything involving a human in the loop is async by
nature, and the current call site is synchronous.
The good news is that most of the work is already done: PolicyContext
(server/src/computer/policy.ts:44-153) is a clean, serialisable, effect-level
object — intent, mcp.effect, file.extension, command, resolved element. That
is a better PDP boundary than most systems that set out to build one.
Concrete shape
export type PolicyDecider = (
context: PolicyContext,
) => PolicyDecision | Promise<PolicyDecision>;
Default stays exactly what happens now:
const decide: PolicyDecider = options.decide
?? ((ctx) => evaluateActionPolicy(options.policy(), ctx));
// ...
const decision = await decide(context);
Line 523 is already inside an async method that awaits write(...) on the next
line, so the await costs nothing structurally.
Two things I'd want written into the contract, not left to the implementer:
- A decider that throws, or exceeds its timeout, is a refusal. Same discipline as
matches()and itsonErrorargument (policy.ts:210-243). A remote PDP that is
merely unreachable must not be a way to get an action through, and the timeout should
be required rather than optional. dry-runstays in the gateway, not in the decider. Otherwise an external decider
can silently disable enforcement for the whole deployment, which is the one thing this
seam must not make possible.
What I would not touch
- Snapshot resolution stays server-side (
gateway.ts:469-471). A decider receives the
element the server resolved, never what the caller claimed. The comment at
policy.ts:38-42is the reason the whole thing works and an external decider must not
get a chance to weaken it. - The gateway still writes the audit row before forwarding (
gateway.ts:524-535),
whoever decided. The trail records the decision, not the decider's opinion of it.
One question before any of this
policy.ts:4-12 says this mirrors the policy engine in CopilotKit's enterprise agent
gateway, deliberately, so a rule means the same thing in both. Does that engine already
have a pluggable decision point — and if not, is divergence here something you'd rather
avoid? If so, say and I'll drop it; that's a good reason.
Otherwise I'm happy to send the PR: default path byte-for-byte unchanged, the type, the
fail-closed timeout, and one worked example decider.
Disclosure: I build an authorization service for agents, so an external PDP is
obviously useful to me. I've tried to write this as it would stand without that —
push back if it reads otherwise.
- 主要语言
- TypeScript
- 星标
- 6.6k
- 派生
- 914
- 平均合并
- 17 小时 52 分钟
- 30 天内合并 PR
- 155
环境准备
- 提供 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 没有贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
CopilotKit/OpenBot 的其他 Issue
-
Wrong key location at startup可能已有人在做 @charan-rathore 今天认领。 未关闭
难度 1/5 1 小时以内 新手友好度 68/100
CopilotKit/OpenBot#800 ·
维护者通常 1 天内回复
-
setup-learning.ts fails on Windows with "spawn npx.cmd EINVAL"可能已有人在做 @Caldas 于 3 天前认领。 未关闭
难度 2/5 1-3 小时 新手友好度 80/100
CopilotKit/OpenBot#792 ·
维护者通常 1 天内回复
-
Remote AG-UI Bots can't use the browser in chat: `computer_*` calls refused as "unavailable for this headless turn"可能已有人在做 关联的 PR 仍在进行中或已合并。 未关闭
难度 3/5 1-2 天 新手友好度 68/100
CopilotKit/OpenBot#804 ·
维护者通常 1 天内回复
-
Five out-of-subject findings from PR #770's tier-3 review (credential-revoking tests, single-user boot gate, OAuth client rotation, shared-use atomicity, untyped server/tests)可能已有人在做 关联的 PR 仍在进行中或已合并。 未关闭
难度 4/5 一周以上 新手友好度 22/100
CopilotKit/OpenBot#786 ·
维护者通常 1 天内回复
-
server/tests leaks database connections: 55 files open a pool and never close it可能已有人在做 @adamsiwiec1 于 2 天前认领。 未关闭
难度 3/5 1-2 天 新手友好度 70/100
CopilotKit/OpenBot#785 ·
维护者通常 1 天内回复
查看 CopilotKit/OpenBot 的全部 Issue
相似的 Issue
-
Object parameters lose their structure when resources is enabled可能已有人在做 @asts-top 今天认领。 未关闭
难度 2/5 1-3 小时 新手友好度 74/100
维护者通常 2 天内回复
-
bug
难度 2/5 1-3 小时 新手友好度 88/100
jaegertracing/jaeger-ui#4618 ·
维护者通常 1 天内回复
-
pending close triaging
难度 2/5 1-3 小时 新手友好度 65/100
anomalyco/opencode#56765 · 1 条评论 ·
维护者通常 1 天内回复
-
area:plugins enhancement platform:vscode
难度 2/5 1-3 小时 新手友好度 88/100
anthropics/claude-code#103166 ·
维护者通常 1 天内回复
-
Types: FlashListProps imports ViewabilityConfig from react-native, removed in RN 0.87+可能已有人在做 @efegokdemir 今天认领。 未关闭
难度 2/5 1-3 小时 新手友好度 78/100
Shopify/flash-list#2540 ·