mulmoclaude 0.6.2 → 0.6.4

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.
Files changed (182) hide show
  1. package/README.md +26 -0
  2. package/bin/mulmoclaude.js +11 -1
  3. package/client/assets/JsonEditor-D6WBWLoa.js +10 -0
  4. package/client/assets/JsonEditor-Di5xGeZY.css +1 -0
  5. package/client/assets/_plugin-vue_export-helper-BOai-rQB.js +1 -0
  6. package/client/assets/chunk-D8eiyYIV-LcKZGJv5.js +1 -0
  7. package/client/assets/{html2canvas-CDGcmOD3-Bkf2uOth.js → html2canvas-CDGcmOD3-XVrO-eyz.js} +1 -1
  8. package/client/assets/index-CyBr8Mkr.css +2 -0
  9. package/client/assets/index-zZIqEbNX.js +5106 -0
  10. package/client/assets/{index.es-DqtpmBm8-D9mAh_KQ.js → index.es-DqtpmBm8-DHT6q10o.js} +1 -1
  11. package/client/assets/material-symbols-outlined-DtIK7AQn.woff2 +0 -0
  12. package/client/assets/runtime-protocol-vue-D6kcV0wa.js +1 -0
  13. package/client/assets/{runtime-vue-BVUzgYGA.js → runtime-vue-fFYhnNg3.js} +1 -1
  14. package/client/assets/{vue-C8UuIO9J.js → vue-D4w8THF_.js} +1 -1
  15. package/client/assets/vue-i18n-CQbxVmNs.js +3 -0
  16. package/client/assets/vue.runtime.esm-bundler-BTyIdNAI.js +4 -0
  17. package/client/index.html +10 -10
  18. package/package.json +9 -8
  19. package/server/agent/backend/claude-code.ts +34 -0
  20. package/server/agent/backend/fake-echo.ts +370 -0
  21. package/server/agent/backend/index.ts +16 -1
  22. package/server/agent/config.ts +74 -24
  23. package/server/agent/index.ts +104 -80
  24. package/server/agent/mcpFailureMonitor.ts +167 -0
  25. package/server/agent/mcpPreflight.ts +185 -0
  26. package/server/agent/prompt.ts +50 -359
  27. package/server/agent/stdioHttpShim.ts +171 -0
  28. package/server/agent/stream.ts +12 -1
  29. package/server/api/routes/encore.ts +55 -0
  30. package/server/api/routes/files.ts +22 -0
  31. package/server/api/routes/mulmo-script.ts +19 -1
  32. package/server/api/routes/schedulerHandlers.ts +52 -4
  33. package/server/api/routes/sessions.ts +15 -0
  34. package/server/api/routes/skills.ts +263 -0
  35. package/server/build/dispatcher.mjs +299 -0
  36. package/server/encore/INVARIANTS.md +272 -0
  37. package/server/encore/boot.ts +39 -0
  38. package/server/encore/closure.ts +36 -0
  39. package/server/encore/cycle.ts +276 -0
  40. package/server/encore/dispatch.ts +103 -0
  41. package/server/encore/handlers/amend.ts +99 -0
  42. package/server/encore/handlers/appendNote.ts +74 -0
  43. package/server/encore/handlers/defineEncore.ts +42 -0
  44. package/server/encore/handlers/listTickets.ts +107 -0
  45. package/server/encore/handlers/markStepDone.ts +41 -0
  46. package/server/encore/handlers/markTargetSkipped.ts +33 -0
  47. package/server/encore/handlers/query.ts +138 -0
  48. package/server/encore/handlers/recordValues.ts +44 -0
  49. package/server/encore/handlers/resolveNotification.ts +121 -0
  50. package/server/encore/handlers/setup.ts +81 -0
  51. package/server/encore/handlers/shared.ts +137 -0
  52. package/server/encore/handlers/snooze.ts +87 -0
  53. package/server/encore/handlers/startObligationChat.ts +64 -0
  54. package/server/encore/handlers/startSetupChat.ts +50 -0
  55. package/server/encore/lock.ts +61 -0
  56. package/server/encore/notifier.ts +123 -0
  57. package/server/encore/obligation.ts +25 -0
  58. package/server/encore/paths.ts +78 -0
  59. package/server/encore/reconcile.ts +661 -0
  60. package/server/encore/tick.ts +191 -0
  61. package/server/encore/yaml-fm.ts +63 -0
  62. package/server/events/notifications.ts +19 -91
  63. package/server/index.ts +94 -9
  64. package/server/notifier/engine.ts +102 -1
  65. package/server/notifier/macosReminderAdapter.ts +30 -0
  66. package/server/notifier/runtime-api.ts +41 -1
  67. package/server/notifier/types.ts +15 -2
  68. package/server/plugins/runtime.ts +11 -2
  69. package/server/prompts/index.ts +39 -0
  70. package/server/prompts/system/journal-pointer.md +12 -0
  71. package/server/prompts/system/memory-management-atomic.md +33 -0
  72. package/server/prompts/system/memory-management-topic.md +60 -0
  73. package/server/prompts/system/news-concierge.md +24 -0
  74. package/server/prompts/system/sandbox-tools.md +10 -0
  75. package/server/prompts/system/sources-context.md +16 -0
  76. package/server/prompts/system/system.md +91 -0
  77. package/server/system/announceOptionalDeps.ts +57 -0
  78. package/server/system/appVersion.ts +34 -0
  79. package/server/system/config.ts +17 -1
  80. package/server/system/docker.ts +14 -6
  81. package/server/system/env.ts +18 -5
  82. package/server/system/optionalDeps.ts +129 -0
  83. package/server/utils/cli-flags.d.mts +14 -0
  84. package/server/utils/cli-flags.mjs +53 -0
  85. package/server/utils/files/encore-io.ts +111 -0
  86. package/server/utils/time.ts +6 -0
  87. package/server/workspace/helps/business.md +2 -2
  88. package/server/workspace/helps/encore-dsl.md +482 -0
  89. package/server/workspace/helps/index.md +15 -13
  90. package/server/workspace/helps/mulmoscript.md +3 -3
  91. package/server/workspace/helps/sandbox.md +2 -2
  92. package/server/workspace/hooks/dispatcher.ts +7 -5
  93. package/server/workspace/hooks/provision.ts +6 -3
  94. package/server/workspace/paths.ts +13 -4
  95. package/server/workspace/skills/catalog.ts +355 -0
  96. package/server/workspace/skills/external/catalog.ts +283 -0
  97. package/server/workspace/skills/external/clone.ts +129 -0
  98. package/server/workspace/skills/external/id.ts +194 -0
  99. package/server/workspace/skills/external/install.ts +417 -0
  100. package/server/workspace/skills/external/presets.ts +50 -0
  101. package/server/workspace/skills-preset.ts +29 -17
  102. package/server/workspace/workspace.ts +10 -5
  103. package/src/App.vue +37 -8
  104. package/src/components/FileContentRenderer.vue +102 -9
  105. package/src/components/JsonEditor.vue +160 -0
  106. package/src/components/NotificationBell.vue +35 -3
  107. package/src/components/PluginLauncher.vue +20 -41
  108. package/src/components/RightSidebar.vue +19 -0
  109. package/src/components/SettingsMcpTab.vue +58 -11
  110. package/src/components/SettingsModal.vue +22 -1
  111. package/src/components/StackView.vue +10 -1
  112. package/src/components/TodoExplorer.vue +16 -0
  113. package/src/components/todo/TodoKanbanView.vue +34 -6
  114. package/src/composables/useNotifications.ts +21 -1
  115. package/src/config/apiRoutes.ts +0 -6
  116. package/src/config/mcpCatalog.ts +12 -7
  117. package/src/config/mcpTypes.ts +5 -0
  118. package/src/config/roles.ts +52 -15
  119. package/src/config/systemFileDescriptors.ts +12 -0
  120. package/src/lang/de.ts +108 -12
  121. package/src/lang/en.ts +105 -11
  122. package/src/lang/es.ts +106 -11
  123. package/src/lang/fr.ts +106 -11
  124. package/src/lang/ja.ts +104 -11
  125. package/src/lang/ko.ts +105 -11
  126. package/src/lang/pt-BR.ts +106 -11
  127. package/src/lang/zh.ts +103 -11
  128. package/src/main.ts +1 -0
  129. package/src/plugins/_generated/metas.ts +4 -0
  130. package/src/plugins/_generated/registrations.ts +2 -0
  131. package/src/plugins/_generated/server-bindings.ts +5 -0
  132. package/src/plugins/encore/EncoreDashboard.vue +504 -0
  133. package/src/plugins/encore/EncoreRedirect.vue +116 -0
  134. package/src/plugins/encore/View.vue +36 -0
  135. package/src/plugins/encore/defineEncoreDefinition.ts +74 -0
  136. package/src/plugins/encore/defineEncoreMeta.ts +13 -0
  137. package/src/plugins/encore/index.ts +93 -0
  138. package/src/plugins/encore/manageEncoreDefinition.ts +100 -0
  139. package/src/plugins/encore/manageEncoreMeta.ts +36 -0
  140. package/src/plugins/manageSkills/View.vue +832 -30
  141. package/src/plugins/manageSkills/categories.ts +125 -0
  142. package/src/plugins/manageSkills/meta.ts +30 -0
  143. package/src/plugins/markdown/definition.ts +3 -3
  144. package/src/plugins/meta-types.ts +5 -0
  145. package/src/plugins/presentMulmoScript/Preview.vue +3 -3
  146. package/src/plugins/presentMulmoScript/View.vue +157 -33
  147. package/src/plugins/presentMulmoScript/meta.ts +4 -0
  148. package/src/plugins/scheduler/View.vue +45 -9
  149. package/src/plugins/scheduler/calendarDefinition.ts +6 -2
  150. package/src/plugins/scheduler/multiDayHelpers.ts +95 -0
  151. package/src/plugins/skill/View.vue +1 -5
  152. package/src/plugins/spreadsheet/View.vue +3 -3
  153. package/src/plugins/spreadsheet/definition.ts +1 -1
  154. package/src/plugins/textResponse/Preview.vue +14 -1
  155. package/src/plugins/textResponse/View.vue +39 -24
  156. package/src/plugins/wiki/components/WikiPageBody.vue +4 -0
  157. package/src/router/index.ts +11 -0
  158. package/src/router/pageRoutes.ts +1 -0
  159. package/src/types/encore-dsl/at-expression.ts +120 -0
  160. package/src/types/encore-dsl/at-resolver.ts +32 -0
  161. package/src/types/encore-dsl/cadence.ts +289 -0
  162. package/src/types/encore-dsl/schema.ts +288 -0
  163. package/src/types/notification.ts +2 -1
  164. package/src/types/session.ts +6 -0
  165. package/src/types/sse.ts +5 -0
  166. package/src/types/toolCallHistory.ts +7 -0
  167. package/src/utils/agent/eventDispatch.ts +26 -5
  168. package/src/utils/agent/mcpHint.ts +50 -0
  169. package/src/utils/image/htmlSrcAttrs.ts +117 -13
  170. package/src/utils/session/sessionEntries.ts +8 -32
  171. package/client/assets/PluginScopedRoot-YjvQq0Nn.js +0 -3
  172. package/client/assets/chunk-CernVdwh.js +0 -1
  173. package/client/assets/chunk-D8eiyYIV-CAXpUwLd.js +0 -1
  174. package/client/assets/index-BwrlMMHr.js +0 -5005
  175. package/client/assets/index-CvvNuegU.css +0 -2
  176. package/client/assets/material-symbols-outlined-BOZVWuR3.woff2 +0 -0
  177. package/client/assets/runtime-protocol-vue-C1To4M3t.js +0 -1
  178. package/client/assets/vue.runtime.esm-bundler-DQ8Kjjui.js +0 -4
  179. package/server/api/routes/notifications.ts +0 -195
  180. package/server/notifier/legacy-adapters.ts +0 -76
  181. package/server/workspace/hooks/dispatcher.mjs +0 -300
  182. package/src/composables/useSelectedResult.ts +0 -49
@@ -1,195 +0,0 @@
1
- // PoC push endpoint — proves the server can fire a delayed message
2
- // simultaneously to every open Web tab (pub-sub) and every bridge
3
- // (chat-service `pushToBridge`). Stepping stone for the in-app
4
- // notification center (#144) and external-channel notifications
5
- // (#142); see plans/done/feat-notification-push-scaffold.md for the
6
- // motivation and the production plan.
7
- //
8
- // Usage (basic):
9
- // curl -X POST http://localhost:3001/api/notifications/test \
10
- // -H "Authorization: Bearer $(cat ~/mulmoclaude/.session-token)" \
11
- // -H "Content-Type: application/json" \
12
- // -d '{"message":"hello","delaySeconds":5}'
13
- //
14
- // Usage (with permalink action — #762):
15
- // curl -X POST http://localhost:3001/api/notifications/test \
16
- // -H "Authorization: Bearer $TOKEN" \
17
- // -H "Content-Type: application/json" \
18
- // -d '{"message":"Scheduled task fired","kind":"scheduler",
19
- // "action":{"type":"navigate",
20
- // "target":{"view":"automations",
21
- // "taskId":"finance-daily-briefing"}}}'
22
- //
23
- // For a one-shot "fire one of every target kind" run,
24
- // scripts/dev/fire-sample-notifications.sh drives this endpoint.
25
- //
26
- // PR 4 of feat-encore made `publishNotification()` a thin wrapper
27
- // over the notifier engine, so this route no longer needs injected
28
- // pubsub / bridge deps — bridge push fans out via the legacy
29
- // adapter subscribed to the engine, and `scheduleTestNotification`
30
- // just calls the wrapper.
31
-
32
- import { Router, type Request, type Response } from "express";
33
- import { scheduleTestNotification, type ScheduleNotificationOptions } from "../../events/notifications.js";
34
- import { log } from "../../system/logger/index.js";
35
- import { API_ROUTES } from "../../../src/config/apiRoutes.js";
36
- import {
37
- NOTIFICATION_ACTION_TYPES,
38
- NOTIFICATION_KINDS,
39
- NOTIFICATION_VIEWS,
40
- type NotificationAction,
41
- type NotificationKind,
42
- type NotificationTarget,
43
- } from "../../../src/types/notification.js";
44
- import { isRecord } from "../../utils/types.js";
45
-
46
- interface TestRequestBody {
47
- message?: unknown;
48
- body?: unknown;
49
- delaySeconds?: unknown;
50
- transportId?: unknown;
51
- kind?: unknown;
52
- action?: unknown;
53
- }
54
-
55
- interface TestResponse {
56
- firesAt: string;
57
- delaySeconds: number;
58
- }
59
-
60
- const KIND_SET = new Set<string>(Object.values(NOTIFICATION_KINDS));
61
- const VIEW_SET = new Set<string>(Object.values(NOTIFICATION_VIEWS));
62
-
63
- function parseKind(value: unknown): NotificationKind | undefined {
64
- if (typeof value !== "string") return undefined;
65
- return KIND_SET.has(value) ? (value as NotificationKind) : undefined;
66
- }
67
-
68
- // `path` / `slug` / `anchor` / etc. arrive as `unknown` from the JSON
69
- // body. The URL builders in `legacyActionToNavigateTarget` assume each
70
- // field is `string | undefined`; passing a number through (e.g.
71
- // `path: 123`) would crash later inside `setTimeout` — after the 202
72
- // is sent — when `path.split("/")` runs. So validate per-view here:
73
- // any required field that isn't a non-empty string, or any optional
74
- // field that is present but isn't a string, drops the whole action.
75
- // The notification still fires; it just lands without a click target.
76
- function isOptionalString(value: unknown): boolean {
77
- return value === undefined || typeof value === "string";
78
- }
79
-
80
- function asOptionalString(value: unknown): string | undefined {
81
- return typeof value === "string" ? value : undefined;
82
- }
83
-
84
- // Per-view validators — split out so the dispatcher stays under the
85
- // cognitive-complexity threshold. Each validator returns either the
86
- // typed target or `undefined` for any field that doesn't match the
87
- // `src/types/notification.ts` discriminated union.
88
-
89
- function validateChatTarget(value: Record<string, unknown>): NotificationTarget | undefined {
90
- // sessionId required: chat without it would bounce off the catch-all redirect.
91
- if (typeof value.sessionId !== "string" || value.sessionId.length === 0) return undefined;
92
- if (!isOptionalString(value.resultUuid)) return undefined;
93
- return { view: NOTIFICATION_VIEWS.chat, sessionId: value.sessionId, resultUuid: asOptionalString(value.resultUuid) };
94
- }
95
-
96
- function validateTodosTarget(value: Record<string, unknown>): NotificationTarget | undefined {
97
- if (!isOptionalString(value.itemId)) return undefined;
98
- return { view: NOTIFICATION_VIEWS.todos, itemId: asOptionalString(value.itemId) };
99
- }
100
-
101
- function validateAutomationsTarget(value: Record<string, unknown>): NotificationTarget | undefined {
102
- if (!isOptionalString(value.taskId)) return undefined;
103
- return { view: NOTIFICATION_VIEWS.automations, taskId: asOptionalString(value.taskId) };
104
- }
105
-
106
- function validateSourcesTarget(value: Record<string, unknown>): NotificationTarget | undefined {
107
- if (!isOptionalString(value.slug)) return undefined;
108
- return { view: NOTIFICATION_VIEWS.sources, slug: asOptionalString(value.slug) };
109
- }
110
-
111
- function validateFilesTarget(value: Record<string, unknown>): NotificationTarget | undefined {
112
- if (!isOptionalString(value.path)) return undefined;
113
- return { view: NOTIFICATION_VIEWS.files, path: asOptionalString(value.path) };
114
- }
115
-
116
- function validateWikiTarget(value: Record<string, unknown>): NotificationTarget | undefined {
117
- if (!isOptionalString(value.slug)) return undefined;
118
- if (!isOptionalString(value.anchor)) return undefined;
119
- return { view: NOTIFICATION_VIEWS.wiki, slug: asOptionalString(value.slug), anchor: asOptionalString(value.anchor) };
120
- }
121
-
122
- function validateNavigateTarget(value: unknown): NotificationTarget | undefined {
123
- if (!isRecord(value)) return undefined;
124
- if (typeof value.view !== "string" || !VIEW_SET.has(value.view)) return undefined;
125
- switch (value.view) {
126
- case NOTIFICATION_VIEWS.chat:
127
- return validateChatTarget(value);
128
- case NOTIFICATION_VIEWS.todos:
129
- return validateTodosTarget(value);
130
- case NOTIFICATION_VIEWS.calendar:
131
- return { view: NOTIFICATION_VIEWS.calendar };
132
- case NOTIFICATION_VIEWS.automations:
133
- return validateAutomationsTarget(value);
134
- case NOTIFICATION_VIEWS.sources:
135
- return validateSourcesTarget(value);
136
- case NOTIFICATION_VIEWS.files:
137
- return validateFilesTarget(value);
138
- case NOTIFICATION_VIEWS.wiki:
139
- return validateWikiTarget(value);
140
- default:
141
- return undefined;
142
- }
143
- }
144
-
145
- /** Exported for unit tests. Production callers go through
146
- * `createNotificationsRouter` and never see this function directly. */
147
- export function parseAction(value: unknown): NotificationAction | undefined {
148
- if (!isRecord(value)) return undefined;
149
- if (value.type === NOTIFICATION_ACTION_TYPES.none) {
150
- return { type: NOTIFICATION_ACTION_TYPES.none };
151
- }
152
- if (value.type !== NOTIFICATION_ACTION_TYPES.navigate) return undefined;
153
- const target = validateNavigateTarget(value.target);
154
- if (!target) return undefined;
155
- return { type: NOTIFICATION_ACTION_TYPES.navigate, target };
156
- }
157
-
158
- function parseBody(body: TestRequestBody): ScheduleNotificationOptions {
159
- const opts: ScheduleNotificationOptions = {};
160
- if (typeof body.message === "string" && body.message.length > 0) {
161
- opts.message = body.message;
162
- }
163
- if (typeof body.body === "string" && body.body.length > 0) {
164
- opts.body = body.body;
165
- }
166
- if (typeof body.delaySeconds === "number") {
167
- opts.delaySeconds = body.delaySeconds;
168
- }
169
- if (typeof body.transportId === "string" && body.transportId.length > 0) {
170
- opts.transportId = body.transportId;
171
- }
172
- const kind = parseKind(body.kind);
173
- if (kind) opts.kind = kind;
174
- const action = parseAction(body.action);
175
- if (action) opts.action = action;
176
- return opts;
177
- }
178
-
179
- export function createNotificationsRouter(): Router {
180
- const router = Router();
181
- router.post(API_ROUTES.notifications.test, (req: Request<object, unknown, TestRequestBody>, res: Response<TestResponse>) => {
182
- const opts = parseBody(req.body ?? {});
183
- const scheduled = scheduleTestNotification(opts);
184
- log.info("notifications", "scheduled test push", {
185
- delaySeconds: scheduled.delaySeconds,
186
- firesAt: scheduled.firesAt,
187
- transportId: opts.transportId,
188
- });
189
- res.status(202).json({
190
- firesAt: scheduled.firesAt,
191
- delaySeconds: scheduled.delaySeconds,
192
- });
193
- });
194
- return router;
195
- }
@@ -1,76 +0,0 @@
1
- // Bridge + macOS Reminder side-channel adapters for the notifier engine.
2
- //
3
- // These previously fired inline inside `publishNotification()` (legacy
4
- // `server/events/notifications.ts`). PR 4 of feat-encore relocates them
5
- // to in-process listeners on the engine so the wrapper stays concerned
6
- // only with mapping → engine, and any future direct caller of
7
- // `notifier.publish()` automatically gets the same fan-out by setting
8
- // the fields the adapters read.
9
- //
10
- // **No severity-based routing in v1.** The macOS adapter fires for
11
- // every `published` event (gated only by darwin + env flag inside
12
- // `pushToMacosReminder` itself), and the bridge adapter fires only
13
- // when the entry carries a legacy `transportId` in its `pluginData`.
14
- // If a future use case wants severity-driven routing, the engine
15
- // already stores `severity` on every entry — the adapters can grow
16
- // the check without engine changes.
17
- //
18
- // Why in-process listeners (`engine.onEvent`) rather than a pubsub
19
- // subscription: the host's `IPubSub` is fan-out-only with no
20
- // server-side subscribe API. Going through socket.io for an
21
- // in-process notification would mean shipping the event out and
22
- // reading it back through a websocket round-trip, which is silly.
23
-
24
- import { onEvent } from "./engine.js";
25
- import { isLegacyNotifierPluginData } from "../events/notifications.js";
26
- import { pushToMacosReminder } from "../system/macosNotify.js";
27
- import { log } from "../system/logger/index.js";
28
- import type { NotifierEntry } from "./types.js";
29
-
30
- export type PushToBridge = (transportId: string, chatId: string, message: string) => void;
31
-
32
- export interface LegacyAdapterDeps {
33
- pushToBridge: PushToBridge;
34
- }
35
-
36
- /** Format the bridge message identically to the legacy inline path so
37
- * Telegram / CLI subscribers see the same text shape they always did
38
- * (icon + title + optional body). */
39
- function formatBridgeMessage(entry: NotifierEntry): string {
40
- const legacy = isLegacyNotifierPluginData(entry.pluginData) ? entry.pluginData : null;
41
- // U+2705 (white heavy check mark) for the agent kind, U+1F514 (bell)
42
- // for everything else — same fallback the legacy formatter used.
43
- const icon = legacy?.kind === "agent" ? "✅" : "\u{1F514}";
44
- const parts = [icon, entry.title];
45
- if (entry.body) parts.push(entry.body);
46
- return parts.join(" ");
47
- }
48
-
49
- /** Wire the adapters as in-process listeners on the notifier engine.
50
- * Returns an unsubscribe function for tests / teardown. */
51
- export function startLegacyAdapters(deps: LegacyAdapterDeps): () => void {
52
- return onEvent((event) => {
53
- if (event.type !== "published") return;
54
- const { entry } = event;
55
- // macOS sink is a no-op outside darwin / when
56
- // DISABLE_MACOS_REMINDER_NOTIFICATIONS=1, so it's safe to fire
57
- // unconditionally. `pushToMacosReminder` itself wraps every
58
- // failure path in try / log.warn.
59
- void pushToMacosReminder(entry.title, entry.body);
60
- const legacy = isLegacyNotifierPluginData(entry.pluginData) ? entry.pluginData : null;
61
- if (legacy?.transportId) {
62
- try {
63
- // chatId is hardcoded — the legacy `chatId` knob on the PoC
64
- // endpoint was a one-caller artifact (only `scheduleTestNotification`
65
- // ever set it) and was removed alongside the migration. If a
66
- // real production caller later needs per-conversation routing,
67
- // it deserves a designed surface, not a recreated PoC field.
68
- deps.pushToBridge(legacy.transportId, "notifications", formatBridgeMessage(entry));
69
- } catch (err) {
70
- // Keep the legacy contract: a failing bridge sink must never
71
- // bubble out of the notifier emit chain.
72
- log.warn("notifier-legacy-adapters", "bridge push failed", { error: String(err) });
73
- }
74
- }
75
- });
76
- }