DSH / Atlas
2026-08-22implementedfeaturearchived 2026-09-30

Fire-and-forget webhook Sessions

Fire-and-forget webhook Session

External repository events need to start ordinary DSH work without making every provider adapter understand Agent presets, Workspace attachment, titles, permissions, and callback teardown. GitHub pull requests becoming ready for review are the first use: a signed event may create a review Session that users can browse under the repository Workspace. Turning this into a durable automation engine would introduce a seco

English

Problem

External repository events need to start ordinary DSH work without making every provider adapter understand Agent presets, Workspace attachment, titles, permissions, and callback teardown. GitHub pull requests becoming ready for review are the first use: a signed event may create a review Session that users can browse under the repository Workspace.

Turning this into a durable automation engine would introduce a second lifecycle beside Sessions: delivery records, execution states, retry and deduplication policy, crash recovery, and an answer to whether HTTP acceptance, prompt admission, Agent idle, or model output means completion. The requested capability needs none of those meanings.

Decision

@deepseek-ai/dsh-webhook owns a two-operation Host runtime: rules register through register(), and authenticated provider adapters call dispatch(). Each matching callback runs independently as arbitrary trusted code and returns null or one Workspace-backed Session request. Dispatch returns before callbacks settle, while effect disposal aborts and drains only the calls it owns.

The runtime stores no provider delivery or execution record. It does not retry, deduplicate, resume callback work, observe Agent status, or collect a result. A repeated delivery may create another Session. WebhookDeliveryId remains available to a rule that deliberately implements idempotency through its own state.

Provider adapters

Authentication belongs to provider adapters. @deepseek-ai/dsh-webhook-github registers one exact route on an injected WebServer, bounds the untouched UTF-8 body, resolves its secret reference per request, verifies X-Hub-Signature-256 before parsing, and passes a signed lossless-JSON object to the runtime. 202 means only verified in-memory dispatch; it precedes rule matching, external calls, and Session creation.

The normal Web composition keeps its UI/API WebServer separate. The GitHub example mounts another WebServer and its adapter in a group that isolates only webServer, so a reverse proxy can expose the webhook port without exposing /api, WebSockets, or frontend files.

Patch loading anchors relative plugin names in inserted rows to the patch file. The same ./github-ready-review-rule.mjs entry therefore works from a development --patch overlay and from a permanent profile patch without changing the rule into a package.

Session creation

A rule result names a local Workspace path, title, text prompt, agent preset, permission preset, and optional explicit provider/model route with an output cap. Without that route, the runtime snapshots the complete live default, including reasoning effort, until the first request records its durable header. It validates presets before mutation, resolves or creates the canonical Workspace, creates the Agent with that path as Session cwd, mounts the preset before publication, and attaches the Session before admitting the prompt.

The initial follow-up is an ordinary durable user-role message whose source records the webhook provider, source, delivery, and rule identifiers. Its inbox insertion is the webhook operation's last boundary. Ordinary Session persistence and Agent lifecycle own later work; the runtime neither flushes specially nor waits for a turn.

Alternatives considered

Persist deliveries and execution states. Rejected because pending, admitted, running, and settled require retry, deduplication, crash, and completion semantics that the current capability does not consume.

Acknowledge GitHub after Session creation. Rejected because arbitrary rules may call external systems and exceed the provider's HTTP window; a valid delivery should not couple transport availability to later rule work.

Register the route on the main WebServer. Rejected because operators need to expose webhook ingress without also exposing the browser API. An isolated second instance reuses the existing HTTP module without creating another server implementation.

Restrict rules to a declarative predicate language. Rejected because programmatic rules explicitly need arbitrary external calls. Trusted Cordis plugins already provide the required authority and lifecycle.

Let each adapter create Sessions directly. Rejected because Workspace, preset, permission, title, rollback, and message-source logic would spread across provider packages.

Verification

Package tests pin independent callback execution, fire-and-forget HTTP timing, cancellation and quiescent disposal, request validation, Workspace attachment before prompt admission, rollback, GitHub HMAC and body limits, credential rotation, and exact Loader composition. The assembled Web example sends a signed ready-for-review delivery to an isolated second listener and records the resulting ordinary Workspace conversation.

A real-API e2e test starts the built dsh web CLI with the webhook overlay and isolated listener, synthesizes only the signed inbound GitHub delivery, observes Workspace attachment and the durable message source through the public Web API, and waits for the real DeepSeek response. No DSH service, model adapter, or provider call is replaced by a test double.

Source audits keep execution records, retry timers, dedupe maps, completion events, and Agent-status listeners absent.

Consequences

  • Provider adapters stay small and provider-specific while Session creation has one owner.
  • Users receive ordinary titled Sessions under Web Workspaces rather than a second automation UI.
  • HTTP success intentionally says nothing about downstream matching or Agent success.
  • Crashes and repeated deliveries retain simple at-most-process-lifetime semantics; deployments needing durable automation must add a separately designed subsystem rather than reinterpret this runtime.

中文

Problem

外部仓库事件需要启动普通 DSH 工作,同时不能让每个提供方适配器都理解 Agent preset、Workspace 附加、标题、权限与回调 teardown。GitHub pull request 变为 ready for review 是第一个用途:签名事件可以创建一个评审 Session,用户能在仓库 Workspace 下浏览它。

如果把它变成持久自动化引擎,就会在 Session 旁引入第二套生命周期:交付记录、执行状态、重试与去重策略、崩溃恢复,以及 HTTP 接受、提示词接纳、Agent idle 或模型输出中究竟哪个表示完成。所请求能力不需要其中任何含义。

Decision

@deepseek-ai/dsh-webhook 拥有只有两个操作的 Host runtime:规则通过 register() 注册,已验证身份的提供方适配器调用 dispatch()。每个匹配回调都作为任意受信任代码独立运行,并返回 null 或一个基于 Workspace 的 Session 请求。dispatch 会在回调结算前返回,而 effect disposer 只中止并排空自己拥有的调用。

runtime 不存储提供方交付或执行记录。它不重试、不去重、不恢复回调工作、不观察 Agent 状态,也不收集结果。重复交付可能创建另一个 Session。WebhookDeliveryId 仍可供有意通过自有状态实现幂等性的规则使用。

Provider adapters

身份验证属于提供方适配器。@deepseek-ai/dsh-webhook-github 会在注入的 WebServer 上注册一条精确路由,限制未改动的 UTF-8 body,为每次请求解析密钥引用,在解析前验证 X-Hub-Signature-256,并把签名无损 JSON 对象交给 runtime。202 只表示已验证的内存分发;它先于规则匹配、外部调用和 Session 创建。

普通 Web 组合保持其 UI/API WebServer 独立。GitHub 示例会把另一个 WebServer 及其适配器挂载到只隔离 webServer 的 group 中,因此反向代理可以暴露 webhook 端口,而不暴露 /api、WebSocket 或前端文件。

Patch 加载会把插入行中的相对插件名锚定到 patch 文件。因而同一个 ./github-ready-review-rule.mjs 条目既可用于开发环境的 --patch overlay,也可用于永久 profile patch,而无需把规则改成软件包。

Session creation

规则结果会指定本地 Workspace 路径、标题、文本提示词、agent preset、permission preset,以及可选的明确提供方/模型路由与输出上限。没有明确路由时,runtime 会快照包含推理强度的完整实时默认选择,直到首个请求记录其持久 header。runtime 会在变更状态前验证 preset,解析或创建规范 Workspace,以该路径作为 Session cwd 创建 Agent,在发布前挂载 preset,并在接纳提示词前附加 Session。

初始 follow-up 是普通持久 user-role 消息,并携带 webhook 提供方、来源、交付和规则来源信息。它的 inbox 插入是 webhook 操作的最后边界。之后的工作由普通 Session persistence 与 Agent 生命周期拥有;runtime 既不执行特殊 flush,也不等待轮次。

Alternatives considered

持久化交付与执行状态。 否决,因为 pending、admitted、running 与 settled 需要当前能力没有消费方的重试、去重、崩溃和完成语义。

在 Session 创建后再向 GitHub 确认。 否决,因为任意规则可能调用外部系统并超过提供方 HTTP 时间窗;有效交付不应把传输可用性与后续规则工作耦合。

在主 WebServer 上注册路由。 否决,因为操作者需要暴露 webhook 入口而不同时暴露浏览器 API。隔离的第二个实例会复用现有 HTTP 模块,而不会创建另一套服务器实现。

把规则限制为声明式谓词语言。 否决,因为程序化规则明确需要任意外部调用。受信任 Cordis 插件已经提供所需权限与生命周期。

让每个适配器直接创建 Session。 否决,因为 Workspace、preset、权限、标题、rollback 与来源信息逻辑会散布到各提供方包。

Verification

包级测试固定独立回调执行、fire-and-forget HTTP 时序、取消与静止态释放、请求验证、提示词接纳前的 Workspace 附加、rollback、GitHub HMAC 与 body 限制、凭据轮换和精确 Loader 组合。组装 Web 示例会向隔离的第二监听器发送签名 ready-for-review 交付,并记录所得普通 Workspace 对话。

真实 API e2e 测试会通过带 webhook overlay 与隔离监听器的构建产物启动 dsh web CLI(命令行界面),只合成带签名的入站 GitHub 交付,通过公开 Web API 观察 Workspace 附加与持久来源信息,并等待真实 DeepSeek 响应。测试不会用 test double 替换任何 DSH 服务、模型适配器或提供方调用。

源码审计会保持执行记录、重试 timer、去重 map、完成事件与 Agent 状态监听器不存在。

Consequences

  • 提供方适配器保持小而且只含提供方逻辑,Session 创建只有一个 owner。
  • 用户在 Web Workspace 下获得普通带标题 Session,而不是第二套自动化 UI。
  • HTTP 成功刻意不说明下游匹配或 Agent 成功。
  • 崩溃与重复交付保持简单的进程生命周期内语义;需要持久自动化的部署必须增加单独设计的子系统,而不是重新解释此 runtime。