relay-dsh-plugin-events 0.2.1 → 0.2.2

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/README.md CHANGED
@@ -1,10 +1,12 @@
1
1
  # Relay Events for DeepSeek Harness
2
2
 
3
- > **Now supports the latest DSH `0.1.2-alpha.3`.** Plugin `0.2.1` is verified on DSH `0.1.2-alpha.3`, `0.1.2-alpha.2`, and `0.1.1-rc.2`. [Install it and try the latest DSH](https://www.npmjs.com/package/relay-dsh-plugin-events) · [Compatibility details](docs/dsh-0.1.2-alpha.3.md).
3
+ > **Now supports DSH `0.1.2-rc.1` while retaining `0.1.2-alpha.3` compatibility.** Plugin `0.2.2` is verified on both releases. [Install it from npm](https://www.npmjs.com/package/relay-dsh-plugin-events) · [Compatibility evidence](https://github.com/yangbobo2021/Relay/tree/codex/relay-foundation/dsh-lab/dsh-0.1.2-rc.1-20260903).
4
+
5
+ > **Release channels:** `latest` → `0.2.2`; `next` → `0.2.1-rc.1`.
4
6
 
5
7
  ```bash
6
- npx @deepseek-ai/dsh@0.1.2-alpha.3 plugin --profile web add relay-dsh-plugin-events@0.2.1
7
- npx @deepseek-ai/dsh@0.1.2-alpha.3 web
8
+ npx @deepseek-ai/dsh@0.1.2-rc.1 plugin --profile web add relay-dsh-plugin-events@0.2.2
9
+ npx @deepseek-ai/dsh@0.1.2-rc.1 web
8
10
  ```
9
11
 
10
12
  [![DSH compatibility](https://img.shields.io/badge/DSH-0.1.1--rc.2%20%7C%200.1.2--alpha.2%20%7C%200.1.2--alpha.3-2f7d68)](https://github.com/deepseek-ai/deepseek-harness)
package/README.zh.md CHANGED
@@ -1,10 +1,12 @@
1
1
  # 面向 DeepSeek Harness 的 Relay Events
2
2
 
3
- > **现已支持最新 DSH `0.1.2-alpha.3`。** 插件 `0.2.1` 已在 DSH `0.1.2-alpha.3`、`0.1.2-alpha.2` `0.1.1-rc.2` 上完成兼容验证。[安装插件,立即体验最新版 DSH](https://www.npmjs.com/package/relay-dsh-plugin-events) · [兼容性详情](docs/dsh-0.1.2-alpha.3.md)。
3
+ > **现已支持 DSH `0.1.2-rc.1`,并保留对 `0.1.2-alpha.3` 的兼容。** 插件 `0.2.2` 已在两个版本上完成验证。[ npm 安装](https://www.npmjs.com/package/relay-dsh-plugin-events) · [兼容性证据](https://github.com/yangbobo2021/Relay/tree/codex/relay-foundation/dsh-lab/dsh-0.1.2-rc.1-20260903)。
4
+
5
+ > **发布通道:** `latest` → `0.2.2`;`next` → `0.2.1-rc.1`。
4
6
 
5
7
  ```bash
6
- npx @deepseek-ai/dsh@0.1.2-alpha.3 plugin --profile web add relay-dsh-plugin-events@0.2.1
7
- npx @deepseek-ai/dsh@0.1.2-alpha.3 web
8
+ npx @deepseek-ai/dsh@0.1.2-rc.1 plugin --profile web add relay-dsh-plugin-events@0.2.2
9
+ npx @deepseek-ai/dsh@0.1.2-rc.1 web
8
10
  ```
9
11
 
10
12
  [![DSH 兼容版本](https://img.shields.io/badge/DSH-0.1.1--rc.2%20%7C%200.1.2--alpha.2%20%7C%200.1.2--alpha.3-2f7d68)](https://github.com/deepseek-ai/deepseek-harness)
package/SPEC.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Relay DSH Events Plugin Specification
2
2
 
3
- Status: Accepted for `0.1.0`
3
+ Status: Accepted; `0.2.1` baseline plus Event Productization contracts
4
4
 
5
5
  ## Purpose
6
6
 
@@ -16,6 +16,9 @@ The plugin owns:
16
16
  - Wait, Event, routing-attempt, routing-decision, Delivery, Activation, and Monitor
17
17
  persistence in one local SQLite database;
18
18
  - the `relayEvents` Cordis service and its versioned Router/Monitor provider slots;
19
+ - Agent-authored versioned Wait continuation and immutable matched-Wait Delivery
20
+ snapshots;
21
+ - capability-scoped trusted bound-Event source registration;
19
22
  - exact event-type fallback routing when no Router provider is registered;
20
23
  - delivery to the existing DSH Session, stable Activation identity, and background
21
24
  recovery of queued Deliveries;
@@ -29,50 +32,91 @@ The plugin does not own:
29
32
  - Monitor observation, detection, scheduling, or generated code;
30
33
  - email, CI, or provider-specific Webhook normalization;
31
34
  - conversation creation or an execution backend;
32
- - notification delivery for escalated Events.
35
+ - provider-specific notification transport implementation. Events owns provider
36
+ registration, bounded escalation calls, durable outcomes, receipt IDs, attempt
37
+ counts, and explicit safe retry.
33
38
 
34
39
  ## Public Service Contract
35
40
 
36
41
  `ctx.relayEvents.apiVersion` is `1`. The public service exposes high-level event
37
- operations and two replaceable provider registrations:
42
+ operations, two replaceable providers, and capability-scoped bound sources:
38
43
 
39
44
  - `registerRouter(provider)` accepts exactly one active Router with stable `id` and
40
45
  `route({ event, eventRecord, sessions })`;
41
46
  - `registerMonitorProvider(provider)` accepts exactly one active Monitor provider
42
47
  with `prepare`, `checkMonitor`, and lifecycle-safe disposal;
48
+ - `registerBundleCatalogProvider(provider)` accepts one live, secret-safe Monitor
49
+ Bundle catalog projection; management snapshots keyset-page it independently from
50
+ Event history and never infer creation capabilities from persisted instances;
51
+ - `registerBoundEventSource(provider)` accepts non-overlapping declared Event source
52
+ names and returns an unforgeable capability whose `handleEvent({ event, binding })`
53
+ path can select one active Wait without semantic routing;
43
54
  - Wait/Event operations: `registerWaits`, `cancelWaits`, `listWaits`,
44
55
  `handleEvent`, and `dispatchSession`;
45
56
  - Monitor persistence operations: `beginMonitorCheck`, `completeMonitorCheck`,
46
57
  `failMonitorCheck`, `abandonMonitorCheck`, and `listDueMonitors`. No SQLite
47
58
  handle, mutable map, or DSH Agent object crosses the service boundary.
59
+ - `registerNotificationProvider(provider)` accepts at most one capability-scoped
60
+ escalation notifier. Missing and failed notification attempts remain durable,
61
+ inspectable outcomes and never fabricate a Session.
62
+ - Monitor lifecycle operations inspect, pause, resume, update cadence, run now, and
63
+ stop with optimistic versions and durable terminal actor/reason evidence.
64
+ Rebaseline creates an immutable version or reactivates an identical retained
65
+ version for rollback, always recording a fresh baseline before switching it active.
48
66
 
49
67
  Provider registration is owned by the registering Cordis fiber. Duplicate active
50
68
  providers fail closed. Unloading a provider restores the exact fallback Router or
51
69
  the Monitor-unavailable state without unloading Events.
52
70
 
71
+ Wait continuation is normalized to version 1 and persisted inside the immutable Wait
72
+ card. A Delivery stores the exact matched Wait snapshot and routing evidence at commit
73
+ time. Public Event payload fields cannot assert a trusted binding, and a stale or
74
+ cross-Session binding escalates without claiming a Wait.
75
+
53
76
  ## Reliability
54
77
 
55
- - Event ingestion is idempotent by source identity and fingerprint.
78
+ - Event ingestion is idempotent by source identity and fingerprint. Reusing one
79
+ provider identity with conflicting content fails closed.
80
+ - Capability-scoped sources and bound Monitors may provide a bounded canonical
81
+ correlation key for cross-source convergence. Generic public Event content cannot
82
+ set that key.
56
83
  - Routing decisions are validated against the current Wait snapshot and committed
57
84
  once.
58
85
  - Delivery creation and Wait claim are atomic.
86
+ - Exact routing escalates conflicting cross-Session exclusive matches instead of
87
+ selecting an owner by iteration order. Non-exclusive matches may fan out.
88
+ - Delivery retries reproduce the committed matched Wait snapshots even after the
89
+ Session registers replacement Waits.
59
90
  - Failed admission keeps the same Event, Delivery, and Activation IDs queued.
60
91
  - Admission succeeds when the stable inbox message has been flushed to DSH
61
92
  persistence, not when the model turn finishes. A retry recognizes the persisted
62
93
  Activation message and does not enqueue another follow-up.
63
94
  - Reappearing Monitor trigger keys do not create another Delivery or pause a
64
95
  rearmed Monitor; its replacement Wait stays active.
96
+ - Router failures are durably counted. Exhausting the configured failure budget
97
+ commits one inspectable `escalate` decision without exposing provider errors or
98
+ secrets and without creating a Delivery.
99
+ - A background recovery scan advances both incomplete `received`/`routing` Events
100
+ and queued Deliveries, including after Host restart.
65
101
  - A background recovery scan retries queued Deliveries after failure or Host restart.
66
102
  - Events never create DSH Sessions and never switch the selected Web Session.
67
103
  - Shutdown stops new operations, waits for in-flight work, disposes routes/listeners,
68
104
  and closes SQLite last.
105
+ - The management snapshot includes live and historical registrations plus recent
106
+ Events; management mutations use authoritative versions and never silently apply
107
+ to stale rows.
69
108
 
70
109
  ## Security
71
110
 
72
111
  - Loopback ingress may omit a token. Non-loopback ingress requires an exact Bearer
73
112
  token and fails closed when no token is configured.
74
113
  - Event fields remain untrusted content in the injected DSH envelope.
114
+ - Agent-authored continuation is structurally bounded and remains separate from
115
+ external Event content.
75
116
  - Payload size is bounded before JSON parsing.
117
+ - Admission is globally bounded by a sliding rate window and an in-flight
118
+ concurrency ceiling; overload returns fixed public errors and never exposes
119
+ internal exception text.
76
120
  - Provider-specific signatures are outside this generic ingress boundary.
77
121
 
78
122
  ## Delivery Acceptance
@@ -21,3 +21,22 @@ export function validateMonitorProvider(provider) {
21
21
  }
22
22
  return provider;
23
23
  }
24
+
25
+ export function validateBoundEventSourceProvider(provider) {
26
+ if (!provider || typeof provider !== "object") throw new TypeError("bound Event source provider is required");
27
+ if (!/^[a-z][a-z0-9._-]{0,63}$/u.test(provider.id ?? "")) {
28
+ throw new TypeError("bound Event source provider requires a lowercase stable id");
29
+ }
30
+ if (!Array.isArray(provider.sources) || provider.sources.length === 0) {
31
+ throw new TypeError("bound Event source provider requires at least one source");
32
+ }
33
+ for (const source of provider.sources) {
34
+ if (typeof source !== "string" || source.length === 0 || source.length > 128) {
35
+ throw new TypeError("bound Event source provider has an invalid source");
36
+ }
37
+ }
38
+ if (new Set(provider.sources).size !== provider.sources.length) {
39
+ throw new TypeError("bound Event source provider sources must be unique");
40
+ }
41
+ return provider;
42
+ }
@@ -1,6 +1,8 @@
1
1
  # Events Delivery Acceptance Scenarios
2
2
 
3
- Official DSH reference: `b150a551b8d465e31e418e1b2eaf5e79bbb7d28e`
3
+ Official DSH references: `b150a551b8d465e31e418e1b2eaf5e79bbb7d28e`,
4
+ `0a53fb55bea101816fa226bb964ae2bed71c343b`, and
5
+ `dd6322d604e00eec1ba5e0c8541159906a21094a`.
4
6
 
5
7
  | ID | Scenario | Required result | Evidence |
6
8
  | --- | --- | --- | --- |
@@ -21,4 +23,24 @@ Official DSH reference: `b150a551b8d465e31e418e1b2eaf5e79bbb7d28e`
21
23
  | EVT-015 | Clean unload | New operations fail during shutdown; in-flight work settles; listeners, Remote, service, timers, and SQLite are released. | lifecycle integration |
22
24
  | EVT-016 | Backend neutrality | Events-only, Events+Codex, and Events+Claude compositions contain no backend import or backend-name branch in Events. | static + official DSH |
23
25
  | EVT-017 | Package boundary | Tarball contains built/public artifacts only and imports in a clean directory. | `npm pack` acceptance |
24
-
26
+ | EVT-018 | Wait continuation | Complete and legacy Waits normalize versioned continuation; invalid/oversized nested values fail before replacing prior Waits. | unit + SQLite |
27
+ | EVT-019 | Matched Wait envelope | Delivery contains only immutable matched Wait snapshots, continuation and routing evidence from the committed routing snapshot. | SQLite + inbox contract |
28
+ | EVT-020 | Exclusive conflict | Cross-Session exact matches involving exclusive Waits escalate independent of insertion order and claim nothing. | randomized-order integration |
29
+ | EVT-021 | Non-exclusive fan-out | Exact non-exclusive matches create one atomic Delivery per Session with complete matched cards. | SQLite integration |
30
+ | EVT-022 | Trusted bound source | Only a registered source capability can submit a binding; public payload ownership fields have no authority. | service + protocol security |
31
+ | EVT-023 | Stale/cross-Session binding | Invalid owner, Wait, version, or Session/Wait pair escalates with no claim or Delivery. | SQLite integration |
32
+ | EVT-024 | Schema upgrade | Existing schema v4/v5/v9 storage upgrades through schema v10, preserving Delivery snapshots and adding notification receipt/attempt evidence. | real file migration |
33
+ | EVT-025 | Router failure terminal state | Repeated Router failure reaches the configured budget, commits one inspectable escalation, creates no Delivery, and omits raw provider errors/secrets. | unit + SQLite |
34
+ | EVT-026 | Incomplete Event recovery | The background scan resumes `received`/`routing` Events and reaches a single terminal decision without redelivery or duplicate attempts. | fake-clock integration + restart |
35
+ | EVT-027 | Conflicting provider identity | Reuse of one source delivery identity with different content fails closed, preserves the original terminal Event, and creates no second record. | SQLite security integration |
36
+ | EVT-028 | Monitor lifecycle | Inspect, cadence update, pause, resume, run-now guard, and stop use optimistic versions, preserve baseline, and persist terminal actor/reason/time. | SQLite + Host/client contract |
37
+ | EVT-029 | Historical management snapshot | Live and terminal Waits/Monitors plus recent Events and decisions remain inspectable without reactivating or mutating them. | management Remote + browser |
38
+ | EVT-030 | Escalation notification outcome | One configured provider receives bounded escalation content once; absent and failed providers record visible `unavailable`/`failed` outcomes without leaking raw errors. | service + SQLite |
39
+ | EVT-031 | Trusted cross-source convergence | A trusted webhook and Monitor transition sharing one canonical correlation key create one Event/Delivery regardless of source order; public payload correlation fields have no authority. | connector-monitor composition |
40
+ | EVT-032 | Notification safe retry | Duplicate ingestion never retries a terminal notification; an explicit retry is allowed only for unavailable/failed outcomes, increments attempts, and stores a bounded provider receipt. | service + SQLite + browser |
41
+ | EVT-033 | Global admission budgets | Exact rate and concurrency limits reject only excess Event admission with stable 429/503 responses while management remains usable. | HTTP + concurrency integration |
42
+ | EVT-034 | Management pagination and focus | Stable keyset pages, state/source filters, background refresh, dialog focus trap/return, Escape isolation, and draft preservation work in English and Chinese. | official DSH browser |
43
+ | EVT-035 | Live Bundle catalog | Packed Monitor extensions appear in a distinct creation catalog with live status, type/version/origin, Events, capabilities, lifecycle, permissions, and remediation; no credential/handle fields render. | official DSH browser |
44
+ | EVT-036 | Bundle catalog pagination/localization | A 24-type packed catalog has stable non-overlapping keyset pages; locale change reloads localized provider data; wide/narrow and light/dark layouts remain accessible. | service + official DSH browser |
45
+ | EVT-037 | Immutable rollback | A successful target update adds one version; failed baseline changes nothing; rollback reuses retained content, records a new baseline, and preserves Monitor/Wait identity. | SQLite service |
46
+ | EVT-038 | Empty and fault presentation | Events-only UI renders loading/empty/history states, transitions from zero to one background Event, and exposes retryable failures without fabricating success. | official DSH browser + client contract |
@@ -66,3 +66,93 @@ delivery harness.
66
66
  - A rearmed recurring Monitor now ignores already committed trigger keys without
67
67
  pausing again. The regression checks disappearance/reappearance and the still-active
68
68
  replacement Wait. Shutdown lease release is tested against real SQLite.
69
+
70
+ ## Review 8 — continuation, snapshots, and trusted binding
71
+
72
+ - Added EP-01 continuation normalization with legacy defaults, nested shape and size
73
+ bounds, atomic failure behavior, and authenticated Agent-tool ownership.
74
+ - Persisted the exact matched Wait snapshot and routing evidence on each Delivery so a
75
+ later Wait replacement cannot change the admitted continuation context.
76
+ - Replaced insertion-order exclusive selection with deterministic escalation and kept
77
+ non-exclusive multi-Session fan-out.
78
+ - Added capability-scoped bound Event source registration. Public payload fields cannot
79
+ assert ownership; stale and cross-Session bindings escalate without claims.
80
+ - Exercised a real schema v4 file migration, not an in-memory reconstructed v5 store.
81
+ - The first test command intentionally failed before discovery because `DSH_ROOT` was
82
+ absent; it was not counted as a pass. Verification then used official DSH
83
+ `dd6322d604e00eec1ba5e0c8541159906a21094a` and discovered every expected test with
84
+ zero skip/todo.
85
+ - Mutation review disabled the new exclusive-conflict guard. EP03-006/008 failed in
86
+ the routing validator before any Delivery, proving the acceptance test exercises the
87
+ protection rather than a fixture-only assertion. The guard was restored before the
88
+ final run.
89
+
90
+ ## Review 9 — Router failure terminalization and recovery
91
+
92
+ - Added a configurable Router failure budget whose final attempt commits an
93
+ inspectable escalation instead of leaving an Event permanently in `routing`.
94
+ - The committed decision contains stable public wording and attempt metadata but not
95
+ the Router's raw error text; the regression injects a sentinel secret and proves it
96
+ is absent.
97
+ - Extended background recovery to resume both incomplete Events and queued
98
+ Deliveries. The recovery regression begins with a real rejected ingress call,
99
+ waits for the scheduler's second attempt, then independently reads SQLite-backed
100
+ Event state and asserts exactly two attempts and no Delivery.
101
+
102
+ ## Review 10 — productization and browser delivery
103
+
104
+ - Schema v10 persists notification attempt counts and provider receipts. Tests prove
105
+ duplicate ingress cannot silently retry and only an explicit failed/unavailable
106
+ action can retry; the official browser verifies the second unavailable attempt is
107
+ still visible when no provider exists.
108
+ - Exact rate/concurrency boundaries, fixed public overload/internal errors, stable
109
+ keyset pagination, retention rollback, and Monitor proposal budgets are asserted
110
+ against production paths. Events verification discovered 52/52 tests with zero
111
+ skip/todo, then typecheck, build, and dry-run pack passed.
112
+ - Official DSH `dd6322d604e00eec1ba5e0c8541159906a21094a` accepted the packed management
113
+ suite: English/Chinese, 1280×720 and 1440×900, destructive keyboard dialogs,
114
+ focus return, hostile text, Router and credential lifecycles, pagination, Monitor
115
+ cadence validation/update, terminal/check/trigger evidence, light/dark computed
116
+ WCAG AA contrast, the full user-facing fault matrix, and console/network cleanliness.
117
+ A separate Events-only run proved real empty state and background transition to the
118
+ first durable HTTP Event. Packed Codex and Claude compositions proved the same Event
119
+ stack boots with both backend adapters and opens an existing backend-bound Session.
120
+ - The browser run initially caught Escape propagation, disconnected focus targets,
121
+ an unlabeled Relay filter, and onboarding-mask timing. Each failure remained red
122
+ until the production UI or the test's host-state synchronization was corrected;
123
+ no force-click, skipped case, or weakened Relay assertion was accepted.
124
+ - The extended browser gate also caught tertiary/status colors below AA, a React
125
+ `currentTarget` lifetime crash, and a rapid cadence submit that left the visible
126
+ input at 7200 while persisting 3600. The final assertion waits for the independently
127
+ reloaded Monitor summary, so the old UI-only false positive cannot pass.
128
+
129
+ ## Review 12 — Monitor Bundle catalog and rollback
130
+
131
+ - Service tests page a changing Bundle catalog with opaque keyset cursors, reject
132
+ malformed cursors and one-over limits, and prove provider unload produces a real
133
+ empty catalog rather than cached UI state.
134
+ - SQLite rebaseline tests now update to a second immutable version, force a baseline
135
+ failure and compare the unchanged active version/count, then reactivate the first
136
+ version while recording a third baseline observation.
137
+ - The packed official DSH run registers 22 synthetic types from a separate fixture
138
+ extension plus Time and GitHub. It verifies two non-overlapping catalog pages,
139
+ available/configuration-required states, provider identity, Event/capability data,
140
+ permission/remediation text, and no browser console or resource errors.
141
+ - That browser gate first failed on 3.68:1 dark-theme remediation contrast, then on
142
+ ambiguous controls after two pagers existed, then on English provider content after
143
+ switching to Chinese. Fixes were accepted only after the complete browser matrix
144
+ passed again; static tests pin the locale reload and `zh` to `zh-CN` mapping.
145
+
146
+ ## Review 13 — extraction migration and final package gate
147
+
148
+ - Two real-file migration tests persist the pre-extraction `clock` / `deadline_reached`
149
+ and `github` / `snapshot_changed` shapes, close Events and Monitors, reopen the same
150
+ SQLite file with extension-owned compatibility providers, and assert the original
151
+ IDs, active version, baseline, continuation, Session, and stable Event identity.
152
+ - The root package verifier initially failed before import because it ignored the
153
+ explicit official-DSH checkout and assumed a missing worktree-local directory. It
154
+ now honors `DSH_ROOT`; the restored run builds, packs, clean-installs, and imports
155
+ every selected public entry.
156
+ - Final Events verification discovered 58/58 tests with zero skip/todo, and the root
157
+ integration process discovered 467/467. The external root acceptance report records
158
+ the final artifact SHA-256 so this package does not self-reference its own tarball.