Skip to content

Webhook 事件(Webhook Events) 是一种把经过验证的外部事件转化为 DSH 会话的能力:一次已认证的提供商投递(例如带签名的 GitHub webhook)到达一条可信的程序化规则,规则可以创建一个位于 Web Workspace 内的普通根会话(root Session)来处理该事件。分发是进程内且 fire-and-forget 的——没有投递数据库、队列、重试、去重或 Agent 完成状态。整个家族完全由用户自行组合:没有任何已发布 bundle 接入它。

包角色ctx key
packages/webhook/webhook规则注册表、回调生命周期与基于 Workspace 的会话创建ctx.webhookRuntime
packages/webhook/webhook-github带签名的 GitHub HTTP 适配器(精确 WebServer 路由、有界请求体、HMAC 验证)消费 ctx.webhookRuntime + ctx.webServer

拆分遵循 dsh 众多能力通用的接缝模式:提供商适配器负责认证与归一化投递,规则拥有任意条件与外部调用,运行时拥有回调生命周期与会话创建。这与 packages/hooks/*(Claude Code / Codex shell-hook 桥)是不同的机制——后者在 agent 运行期间触发既有的 hook 配置,而不是从外部事件启动新会话。

运行时:WebhookRuntime ​

ctx.webhookRuntime(见 packages/webhook/webhook/src/index.ts)是 Host 侧的可信程序化规则注册表,只有两个操作:

ts
register<K extends string>(rule: WebhookRule<K>): () => Promise<void>
dispatch<K extends string>(delivery: VerifiedWebhookDelivery<K>): void

WebhookRule<K> 携带一个有 brand 的唯一 id、一个提供商 kind,以及 run(delivery, signal):

ts
interface WebhookRule<K extends string> {
  readonly id: WebhookRuleId
  readonly kind: K
  run(delivery: VerifiedWebhookDelivery<K>, signal: AbortSignal):
    Promise<WebhookSessionRequest | null> | WebhookSessionRequest | null
}

VerifiedWebhookDelivery<K> 携带提供商 kind、配置的 source、提供商的 deliveryId、归一化后的无损 JSON event,以及非负安全整数 receivedAt。运行时在把完整值分发给多于一条规则之前先验证、detach 并冻结它。deliveryId 仅是 provenance——运行时既不存储也不去重它,因此重复投递会再次运行规则。

dispatch() 为匹配的规则拍摄快照,独立调度每条规则,并在任何回调 settle 之前返回;抛错与拒绝按规则隔离。注册是一个 effect,其可等待的 disposer 先隐藏规则,然后中止并排空活动回调——回调必须观察传入的 signal,因为忽略取消的同进程代码无法被强制安全终止。

会话请求 ​

非空的 WebhookSessionRequest 需要绝对 workspacePath、title、prompt、agentPreset 与 permissionPreset。可选的 model 指定显式的提供商/模型路由外加输出 token 上限:

ts
interface WebhookSessionRequest {
  readonly workspacePath: string   // 绝对路径;在 Web Workspace 内创建根会话
  readonly title: string
  readonly prompt: string          // 模型恰好看到的文本——没有私有框架
  readonly agentPreset: string
  readonly permissionPreset: string
  readonly model?: { provider: string; model: string; maxTokens?: number }
}

显式路由使用其适配器的 reasoning 默认值;省略则快照当前完整的部署选择(包括 reasoning effort),直到首个请求记录其持久 header。运行时在变更前验证 presets,解析或创建规范化 Workspace,以该 Workspace 路径作为 SessionHeader.cwd 创建 Agent,在发布前挂载 agent preset,并在应用权限、标题与提示词之前持久附加会话。成功的 Agent.followup() 是 webhook 操作的提交点——消息使用 source.kind: "webhook",并携带 provider、source、delivery 与 rule provenance。运行时不等 idle、不检查回复、也不发布完成状态;此后一切都归普通 Agent 与会话行为所有。

GitHub 适配器 ​

@deepseek-ai/dsh-webhook-github 在注入的 WebServer 上注册恰好一条精确路由(kind: 'exact',见 packages/webhook/webhook-github/src/index.ts)。每个请求它都会解析凭据引用、在 maxBodyBytes 上限内读取原始请求体,并在任何 JSON 解析之前验证 X-Hub-Signature-256 HMAC——尚未进入解析器的请求体不会被信任:

检查次序失败
仅 POST读取请求体之前405 + Allow: POST
Content-Type: application/json(至多一个可选 UTF-8 charset 参数)读取请求体之前415
Content-Length / 流式字节数 ≤ maxBodyBytes读取请求体期间413
X-Hub-Signature-256、x-github-delivery、x-github-event 头存在读取请求体之后400
针对已解析密钥的 HMAC 验证JSON 解析之前401
无损 JSON 对象载荷验证之后400
分发给 ctx.webhookRuntime最后不可用时 503

成功后适配器在内存分发完成后立即应答 202——绝不等待规则或会话 settle。归一化事件是带签名的无损 JSON 对象({ name: eventName, payload }),以 VerifiedWebhookDelivery<'github'> 投递;这是提供商无关的投递,规则会验证它们消费的事件特定字段。当 webhook 密钥凭据不可用时也返回 503。

GitHub review 指南(docs/user/guide/github-review.md)把该路由挂载在隔离的第二个 WebServer 上,因此暴露 webhook 入口绝不会暴露浏览器 API。随附的示例覆盖位于 apps/cli/config/examples/github-review/cordis.yml:

yaml
# Opt-in GitHub webhook overlay over the shipped Web composition. The second
# WebServer lives in an isolated realm so exposing it never exposes the UI API.
- insert:
    - id: webhook-runtime
      name: '@deepseek-ai/dsh-webhook'

    - id: github-ready-review-rule
      name: './github-ready-review-rule.mjs'
      config:
        source: primary-github
        repository: deepseek-harness/deepseek-harness
        workspacePath: !!js process.env.DSH_GITHUB_REVIEW_WORKSPACE ?? process.cwd()
        agentPreset: standard
        permissionPreset: read-only

    - id: github-webhook-ingress
      name: cordis:group
      group: true
      isolate:
        webServer: true
      config:
        - id: github-webhook-server
          name: '@deepseek-ai/dsh-host-webserver'
          config:
            host: '127.0.0.1'
            port: !!js Number(process.env.DSH_GITHUB_WEBHOOK_PORT ?? 3081)
        - id: github-webhook-adapter
          name: '@deepseek-ai/dsh-webhook-github'
          config:
            source: primary-github
            path: /github
            secretEnv: DSH_GITHUB_WEBHOOK_SECRET
            maxBodyBytes: 1048576

规则模块(github-ready-review-rule.mjs)承载组合的逻辑:检查投递、决定是否行动,并返回 WebhookSessionRequest 或 null。

Fire-and-forget 不提供什么 ​

这些注意事项是这个契约诚实的边界——接受并不等于 Agent 成功:

  • 仅进程内 fire-and-forget——崩溃会丢失尚未接纳提示词的规则调用;没有队列、重放或重试。
  • 没有内置去重——重复的提供商投递可能创建重复的会话;需要幂等的规则自己负责。
  • 没有完成结果——HTTP 接受与规则 settle 都不报告 Agent 的成功、idle 或输出。
  • 可信回调必须配合取消——运行时拆除会中止并等待它们,但无法终止任意的同进程代码。
  • Workspace 创建可能比一次失败的会话尝试活得更久——空的 Workspace 会被保留,因为另一个并发调用方可能已经在使用它。

由于这些都没有接入任何已发布 bundle,部署必须显式组合运行时、适配器与规则模块——包括从源码检出组合。

包 ​

包
@deepseek-ai/dsh-webhook
@deepseek-ai/dsh-webhook-github

延伸阅读 ​

  • packages/webhook/README.md——webhook 家族包地图
  • packages/webhook/webhook/README.md——规则接口与会话请求契约
  • packages/webhook/webhook-github/README.md——GitHub 适配器表面
  • docs/subsystems/webhook.md——共享类型与时机保证
  • docs/user/guide/github-review.md——完整的组合式 GitHub review 示例
  • apps/cli/config/examples/github-review/cordis.yml——随附的 opt-in 覆盖层