Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
461aa45
feat(web-push): browser push notifications + PWA — the web app as a f…
BenKurrek Aug 8, 2026
ef5ee69
fix(web-push): address review — redact VAPID secret, byte-budget payl…
BenKurrek Aug 8, 2026
806bea5
Merge remote-tracking branch 'origin/main' into webapp-push-notificat…
BenKurrek Aug 8, 2026
cfcb1d4
fix(web-push): keep SW registration out of the initial /chat bundle; …
BenKurrek Aug 9, 2026
da8dfcd
test(web-push): register the browser channel in the journey coverage …
BenKurrek Aug 9, 2026
a73f1bb
style(web-push): rustfmt the journey-evidence helper
BenKurrek Aug 9, 2026
03ba627
feat(web-push): present the channel as "Web UI" and hide it from the …
BenKurrek Aug 9, 2026
5c1c515
Merge remote-tracking branch 'origin/main' into webapp-push-notificat…
BenKurrek Aug 9, 2026
1b8eada
docs(web-push): fix stale helper name in install-catalog filter comment
BenKurrek Aug 9, 2026
b85f270
test(web-push): pin notification-helper i18n key as noSelectionHelper
BenKurrek Aug 9, 2026
9daee7f
feat(channels): notifications as a first-class channel capability
BenKurrek Aug 9, 2026
da7b048
fix(channels): cargo fmt + drop vendor names from generic-code comments
BenKurrek Aug 9, 2026
07b1925
docs+test(channels): fix notifications doc + serde-default coverage
BenKurrek Aug 9, 2026
f761742
fix(outbound): decouple notification-picker list from model-delivery …
BenKurrek Aug 10, 2026
2afee52
Merge remote-tracking branch 'origin/main' into webapp-push-notificat…
BenKurrek Aug 10, 2026
61c83b5
test(outbound): cover divergent notifications/final_replies at the fi…
BenKurrek Aug 10, 2026
6224358
Merge remote-tracking branch 'origin/main' into webapp-push-notificat…
BenKurrek Aug 10, 2026
3db0e44
Merge remote-tracking branch 'origin/main' into webapp-push-notificat…
BenKurrek Aug 10, 2026
bd5d16b
Merge remote-tracking branch 'origin/main' into webapp-push-notificat…
BenKurrek Aug 10, 2026
ab3a92b
Merge remote-tracking branch 'origin/main' into webapp-push-notificat…
BenKurrek Aug 10, 2026
35b2554
Merge remote-tracking branch 'origin/main' into webapp-push-notificat…
BenKurrek Aug 10, 2026
8a09857
Merge remote-tracking branch 'origin/main' into webapp-push-notificat…
BenKurrek Aug 10, 2026
660a201
Merge remote-tracking branch 'origin/main' into webapp-push-notificat…
BenKurrek Aug 10, 2026
d039119
Merge remote-tracking branch 'origin/main' into webapp-push-notificat…
BenKurrek Aug 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace]
members = [".", "crates/contracts/ironclaw_common", "crates/substrates/ironclaw_observability", "crates/contracts/ironclaw_host_api", "crates/product/ironclaw_host_ingress", "crates/substrates/ironclaw_libsql_runtime", "crates/substrates/ironclaw_filesystem", "crates/domains/ironclaw_attachments", "crates/domains/ironclaw_extractors", "crates/domains/ironclaw_memory", "crates/extensions/packages/memory-native", "crates/extensions/packages/mem0", "crates/events/ironclaw_event_log", "crates/events/ironclaw_event_projections", "crates/events/ironclaw_event_streams", "crates/events/ironclaw_event_store", "crates/extensions/ironclaw_extension_registry", "crates/extensions/ironclaw_extension_host", "crates/extensions/ironclaw_extension_manager", "crates/kernel/ironclaw_processes", "crates/lanes/ironclaw_sandbox", "crates/lanes/ironclaw_mcp", "crates/lanes/ironclaw_wasm", "crates/lanes/ironclaw_wasm_limiter", "crates/kernel/ironclaw_capabilities", "crates/substrates/ironclaw_secrets", "crates/substrates/ironclaw_network", "crates/kernel/ironclaw_host_runtime", "crates/kernel/ironclaw_runtime_policy", "crates/kernel/ironclaw_authorization", "crates/kernel/ironclaw_approvals", "crates/kernel/ironclaw_resources", "crates/domains/ironclaw_auth", "crates/kernel/ironclaw_trust", "crates/kernel/ironclaw_turns", "crates/contracts/ironclaw_loop_contracts", "crates/contracts/ironclaw_extension_contracts", "crates/contracts/ironclaw_product_contracts", "crates/loop/ironclaw_agent_loop", "crates/domains/ironclaw_threads", "crates/contracts/ironclaw_prompt_envelope", "crates/loop/ironclaw_hooks", "crates/loop/ironclaw_loop_host", "crates/loop/ironclaw_turn_runner", "crates/app/ironclaw_config", "crates/product/ironclaw_operator", "crates/app/ironclaw_composition", "crates/domains/ironclaw_identity", "crates/extensions/ironclaw_extension_support", "crates/app/ironclaw_cli", "crates/domains/ironclaw_trace_commons", "crates/product/ironclaw_webui", "crates/product/ironclaw_openai_compat", "crates/domains/ironclaw_conversations", "crates/product/ironclaw_assistant", "crates/extensions/packages/telegram", "crates/extensions/packages/slack", "crates/domains/ironclaw_outbound", "crates/domains/ironclaw_triggers", "crates/app/ironclaw_architecture_tests", "crates/substrates/ironclaw_safety", "crates/domains/ironclaw_skills", "crates/domains/ironclaw_llm", "tools/ironclaw_stress"]
members = [".", "crates/contracts/ironclaw_common", "crates/substrates/ironclaw_observability", "crates/contracts/ironclaw_host_api", "crates/product/ironclaw_host_ingress", "crates/substrates/ironclaw_libsql_runtime", "crates/substrates/ironclaw_filesystem", "crates/domains/ironclaw_attachments", "crates/domains/ironclaw_extractors", "crates/domains/ironclaw_memory", "crates/extensions/packages/memory-native", "crates/extensions/packages/mem0", "crates/events/ironclaw_event_log", "crates/events/ironclaw_event_projections", "crates/events/ironclaw_event_streams", "crates/events/ironclaw_event_store", "crates/extensions/ironclaw_extension_registry", "crates/extensions/ironclaw_extension_host", "crates/extensions/ironclaw_extension_manager", "crates/kernel/ironclaw_processes", "crates/lanes/ironclaw_sandbox", "crates/lanes/ironclaw_mcp", "crates/lanes/ironclaw_wasm", "crates/lanes/ironclaw_wasm_limiter", "crates/kernel/ironclaw_capabilities", "crates/substrates/ironclaw_secrets", "crates/substrates/ironclaw_network", "crates/kernel/ironclaw_host_runtime", "crates/kernel/ironclaw_runtime_policy", "crates/kernel/ironclaw_authorization", "crates/kernel/ironclaw_approvals", "crates/kernel/ironclaw_resources", "crates/domains/ironclaw_auth", "crates/kernel/ironclaw_trust", "crates/kernel/ironclaw_turns", "crates/contracts/ironclaw_loop_contracts", "crates/contracts/ironclaw_extension_contracts", "crates/contracts/ironclaw_product_contracts", "crates/loop/ironclaw_agent_loop", "crates/domains/ironclaw_threads", "crates/contracts/ironclaw_prompt_envelope", "crates/loop/ironclaw_hooks", "crates/loop/ironclaw_loop_host", "crates/loop/ironclaw_turn_runner", "crates/app/ironclaw_config", "crates/product/ironclaw_operator", "crates/app/ironclaw_composition", "crates/domains/ironclaw_identity", "crates/extensions/ironclaw_extension_support", "crates/app/ironclaw_cli", "crates/domains/ironclaw_trace_commons", "crates/product/ironclaw_webui", "crates/product/ironclaw_openai_compat", "crates/domains/ironclaw_conversations", "crates/product/ironclaw_assistant", "crates/extensions/packages/telegram", "crates/extensions/packages/slack", "crates/extensions/packages/web-push", "crates/domains/ironclaw_outbound", "crates/domains/ironclaw_triggers", "crates/domains/ironclaw_web_push", "crates/app/ironclaw_architecture_tests", "crates/substrates/ironclaw_safety", "crates/domains/ironclaw_skills", "crates/domains/ironclaw_llm", "tools/ironclaw_stress"]
default-members = ["crates/app/ironclaw_cli"]
exclude = [
# Standalone helper binary, `[workspace]`-rooted and never built here (it
Expand Down Expand Up @@ -198,6 +198,8 @@ ironclaw_webui = { path = "crates/product/ironclaw_webui", version = "0.1.0" }
# constants; Slack pairing/actor-resolution int-tier tests need them directly.
ironclaw_slack_extension = { path = "crates/extensions/packages/slack", version = "0.1.0" }
ironclaw_telegram_extension = { path = "crates/extensions/packages/telegram", version = "0.1.0" }
ironclaw_web_push = { path = "crates/domains/ironclaw_web_push", version = "0.1.0" }
ironclaw_web_push_extension = { path = "crates/extensions/packages/web-push", version = "0.1.0" }
ironclaw_config = { path = "crates/app/ironclaw_config", version = "0.1.0" }
ironclaw_openai_compat = { path = "crates/product/ironclaw_openai_compat", version = "0.1.0"}
ironclaw_threads = { path = "crates/domains/ironclaw_threads", version = "0.1.0" }
Expand Down
2 changes: 1 addition & 1 deletion FEATURE_PARITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,7 @@ Trace Commons issuer/TenantCtx note: the server-side `zmanian/tracedao-server` s
| Control UI i18n | ✅ | ❌ | P3 | English, Chinese, Portuguese; expanded with Persian (fa), Dutch (nl), Vietnamese (vi), Italian (it), Arabic (ar), Thai (th), Traditional Chinese (zh-TW) |
| WebChat theme sync | ✅ | ❌ | P3 | Sync with system dark/light mode |
| Partial output on abort | ✅ | ❌ | P2 | Preserve partial output when aborting |
| PWA + Web Push | ✅ | ❌ | P3 | PWA install + Web Push notifications for Gateway chat |
| PWA + Web Push | ✅ | ✅ | P3 | PWA install (root-scope service worker) + Web Push notifications: the `web-push` channel delivers RFC 8030/8291/8292 browser pushes for automation notices and model-directed deliveries |
| Talk Mode (browser realtime voice) | ✅ | ❌ | P3 | OpenAI Realtime + Google Live WebSocket; Gateway-minted ephemeral secrets; backend realtime relay |
| Steer queued messages | ✅ | ❌ | P3 | Steer action on queued messages injects follow-up into active run without retyping |
| Quick Settings dashboard | ✅ | ❌ | P3 | Refreshed grid + presets + quick-create flows + assistant avatar overrides |
Expand Down
19 changes: 10 additions & 9 deletions crates/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Do not eagerly load every crate guide. Route, then read.
| [`contracts/`](./contracts/AGENTS.md) | Neutral vocabulary and ports — the leaf tier; nothing executes, persists, or names a vendor. | You are adding or changing a shared type, identity, port trait, or DTO that more than one tier must see. |
| [`substrates/`](./substrates/AGENTS.md) | Privileged mechanisms the kernel mediates: filesystem, libSQL admission, secrets, network, safety scanning, observability macros. | You are changing storage/network/secret/scanning *mechanism*, not who may use it. |
| [`events/`](./events/AGENTS.md) | What already happened: redacted evidence vocabulary, durable stores, replay-derived projections, admission-checked streams. | You are changing event vocabulary, event persistence, a read model, or stream delivery. |
| [`domains/`](./domains/AGENTS.md) | Typed record/service owners behind the kernel: threads, conversations, triggers, memory, skills, auth, attachments, extractors, identity, llm, trace_commons, outbound. | You are changing a domain's record grammar, service contract, or invariants. |
| [`domains/`](./domains/AGENTS.md) | Typed record/service owners behind the kernel: threads, conversations, triggers, memory, skills, auth, attachments, extractors, identity, llm, trace_commons, outbound, web_push. | You are changing a domain's record grammar, service contract, or invariants. |
| [`kernel/`](./kernel/AGENTS.md) | The authority perimeter, one crate per mediation stage: trust → authorization → approvals → resources → runtime_policy → capabilities → processes → turns → host_runtime. | You are changing what is *allowed to happen* or how recovery stays safe. |
| [`lanes/`](./lanes/AGENTS.md) | Execution for already-authorized work: wasm, wasm_limiter, mcp, sandbox. | You are changing how an approved invocation physically runs. |
| [`loop/`](./loop/AGENTS.md) | Replaceable agent behavior and its hosting: agent_loop, loop_host, turn_runner, hooks. | You are changing what the agent decides next, or the drivers/port adapters that host it. |
Expand All @@ -62,11 +62,11 @@ its own layer or below (dev-dependencies are outside the matrix):
| Layer (low → high) | May depend on | Crates today |
| --- | --- | --- |
| `contracts` | contracts | 6 |
| `substrates` | contracts, substrates | 27 |
| `substrates` | contracts, substrates | 28 |
| `runtimes` | + runtimes | 5 |
| `kernel` | + kernel | 9 |
| `loops` | + loops | 5 |
| `products` | + products | 7 |
| `products` | + products | 8 |
| `app` | + app (everything) | 5 |

The standing-exception list (`LAYER_MATRIX_EXCEPTIONS`, same file) is
Expand All @@ -81,7 +81,7 @@ two do not always rhyme:
- `lanes/` crates are `runtimes`-layer; `loop/` crates are `loops`-layer.
- `extensions/` is deliberately *vertical*: registry = substrates, support =
runtimes, host = loops, manager = products; under `packages/`, the channel
adapter crates (slack, telegram) are products and the memory provider
adapter crates (slack, telegram, web-push) are products and the memory provider
crates (memory-native, mem0) are substrates.
- Two placement surprises: `product/ironclaw_host_ingress` and
`app/ironclaw_config` are `substrates`-layer.
Expand All @@ -92,19 +92,20 @@ files carry their members' exact layers.

## Workspace facts

**64 packages**: 62 under `crates/`, plus the root package
**66 packages**: 64 under `crates/`, plus the root package
`ironclaw_integration_tests` (the in-process Reborn integration suite,
`tests/integration/`) and `tools/ironclaw_stress`. One documented exclusion:
`tools/ironclaw_silk_decoder`, a standalone helper that is
workspace-`exclude`d. Zero crates sit flat under `crates/` and zero owned
placement exceptions remain. The gate is
`python3 scripts/ci/check-target-tree.py`, which compares the workspace
against the documented tree (PROPOSAL §5); on 2026-08-05 it reports:
`target tree: OK (64 workspace members against 64 documented packages, 1
documented exclusion(s), 0 owned exception(s))`.
`target tree: OK (66 workspace members against 66 documented packages, 1
documented exclusion(s), 0 owned exception(s))` (re-derived 2026-08-08 with the
web-push channel's two crates).

Under `crates/extensions/packages/`, 14 package directories: 4 are workspace
crates (`slack`, `telegram`, `memory-native`, `mem0`) and 10 are data-only
Under `crates/extensions/packages/`, 15 package directories: 5 are workspace
crates (`slack`, `telegram`, `web-push`, `memory-native`, `mem0`) and 10 are data-only
(manifest + prompts/schemas, some with prebuilt WASM): github, gmail, the
five google-*, nearai-mcp, notion-mcp, web-access. Every package directory —
data-only ones included — carries its own `README.md`, so the read order
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,14 @@ fn reborn_contracts_crates_carry_a_checked_size_ceiling() {
// `ActivePreferenceTargetCodecs` port beside its sibling
// `PreferenceTargetCodec` — a trait plus a test-shape blanket impl,
// no logic. Count read from this test's own failure message.
("ironclaw_extension_contracts", 7_748),
// 7_748 -> 7_752 (2026-08-08, web-push channel): +4 lines for the
// `VapidAuthorization` arm in the channel egress injection validator
// — schema vocabulary only; signing lives in ironclaw_host_runtime.
// 7_752 -> 7_758 (2026-08-09, notifications capability): +6 lines for the
// `ChannelDescriptor.notifications` field + its doc — a manifest capability
// declaration only; notification delivery gating lives in
// ironclaw_outbound (DeliveryTargetCapabilities) and product resolution.
("ironclaw_extension_contracts", 7_758),
// Raised 17_501 -> 18_570 by #6831 (standardized messaging framework):
// the growth is the `messaging` vocabulary — the StandardMessagingOp
// enum, the 12-code error taxonomy, compiled-in canonical schema/prompt
Expand All @@ -636,7 +643,16 @@ fn reborn_contracts_crates_carry_a_checked_size_ceiling() {
// sandbox transport now exposes graceful lifecycle release. This is
// contract vocabulary; execution and provider cleanup remain in the
// sandbox runtime lane.
("ironclaw_host_api", 18_799),
// Raised 18_799 -> 18_832 by the web-push channel: the
// `RuntimeCredentialTarget::VapidAuthorization` injection kind and the
// `VapidCredentialMaterialV1` material schema (RFC 8292 vocabulary,
// declarations only); ES256 signing stays at the host egress
// credential chokepoint in ironclaw_host_runtime.
// 18_832 -> 18_922 (web-push review): `VapidCredentialMaterialV1` gained
// a redacting `Debug`, a `validate_shape` fallible shape check, and a
// dependency-free base64url decode helper — all declaration/validation
// on the type's own shape, no execution.
("ironclaw_host_api", 18_922),
// 14_479 -> 13_949 (2026-08-07, #7157): downward re-capture after the
// delivery-heuristic vocabulary (stored trigger delivery targets and
// their run-profile plumbing) left this crate with the two-lane
Expand Down Expand Up @@ -680,7 +696,18 @@ fn reborn_contracts_crates_carry_a_checked_size_ceiling() {
// growth is the three admin scrape request DTOs and the wire
// `RebornListThreadsResponse` reuse — declarations only; authorization,
// audit, and artifact building stay in ironclaw_assistant.
("ironclaw_product_contracts", 15_800),
// Raised 15_800 -> 15_840 by the web-push channel: the browser
// enrollment wire DTO family (`RebornWebPush*` status/subscribe/
// unsubscribe shapes) — declarations only; validation and storage stay
// in ironclaw_web_push and ironclaw_assistant.
// 15_840 -> 15_879: the web_push descriptor module (the status view +
// subscribe/unsubscribe command descriptors) joined per the
// transport/product boundary — new feature descriptors are declared
// here, not added to the frozen webui→assistant residue.
// 15_879 -> 15_885 (review): the `endpoint_digest` field + its doc on
// `RebornWebPushSubscriptionInfo` for account-scoped enrollment
// correlation.
("ironclaw_product_contracts", 15_885),
("ironclaw_prompt_envelope", 832),
];

Expand Down Expand Up @@ -1446,8 +1473,14 @@ fn reborn_cli_binary_crate_stays_separate_from_v1_root() {
"ironclaw_webui",
"ironclaw_slack_extension",
"ironclaw_telegram_extension",
// The web-push channel package (adapter/codec/target provider) and
// its domain crate: the binary constructs the adapter around the
// late-bound `WebPushRuntimeSlot` (a domain type) and hands the
// slot to composition, which installs storage at assembly.
"ironclaw_web_push",
"ironclaw_web_push_extension",
],
"ironclaw should enter Reborn through ironclaw_composition (assembled runtime), ironclaw_operator (operator/admin control-plane), ironclaw_host_api (neutral provider DTO contracts), ironclaw_extension_contracts (the extension tier's half of those neutral contracts, since WS1.3), ironclaw_product_contracts (the product tier's half, since WS1.4), ironclaw_config (boot-config contract), ironclaw_trace_commons (contributor-side TraceCommons client extracted from the legacy monolith), ironclaw_auth (auth-owned contracts used by binary-assembled first-party credential wiring), and ironclaw_webui (host-owned WebUI serve lifecycle) — plus ironclaw_extension_host (the NativeExtensionFactory contract), ironclaw_extension_manager (the extension/ironhub command surface, since WS2.4) and concrete extension crates for the binary-assembled native factory registry (DEL-7: only the binary and tests may link concrete extension crates). Adding any other workspace crate here re-opens speculative public API access to internal Reborn types.",
"ironclaw should enter Reborn through ironclaw_composition (assembled runtime), ironclaw_operator (operator/admin control-plane), ironclaw_host_api (neutral provider DTO contracts), ironclaw_extension_contracts (the extension tier's half of those neutral contracts, since WS1.3), ironclaw_product_contracts (the product tier's half, since WS1.4), ironclaw_config (boot-config contract), ironclaw_trace_commons (contributor-side TraceCommons client extracted from the legacy monolith), ironclaw_auth (auth-owned contracts used by binary-assembled first-party credential wiring), and ironclaw_webui (host-owned WebUI serve lifecycle) — plus ironclaw_extension_host (the NativeExtensionFactory contract), ironclaw_extension_manager (the extension/ironhub command surface, since WS2.4), concrete extension crates for the binary-assembled native factory registry (DEL-7: only the binary and tests may link concrete extension crates), and ironclaw_web_push (the domain type behind the web-push binding's late-bound runtime slot). Adding any other workspace crate here re-opens speculative public API access to internal Reborn types.",
);
assert_workspace_deps_exactly(
&dependencies_all_kinds,
Expand Down
Loading
Loading