@jami-studio/core 0.92.27 → 0.92.29

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 (56) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +34 -0
  3. package/corpus/core/package.json +2 -1
  4. package/corpus/core/src/deploy/build.ts +120 -2
  5. package/corpus/core/src/event-bus/bus.ts +138 -140
  6. package/corpus/core/src/event-bus/registry.ts +81 -79
  7. package/corpus/core/src/file-upload/registry.ts +135 -125
  8. package/corpus/core/src/notifications/registry.ts +218 -216
  9. package/corpus/core/src/private-blob/registry.ts +246 -242
  10. package/corpus/core/src/secrets/register.ts +132 -129
  11. package/corpus/core/src/shared/global-scope.ts +75 -0
  12. package/corpus/core/src/sharing/registry.ts +193 -194
  13. package/corpus/core/src/tracking/providers.ts +425 -422
  14. package/corpus/core/src/tracking/registry.ts +102 -102
  15. package/dist/collab/routes.d.ts +1 -1
  16. package/dist/deploy/build.d.ts +44 -0
  17. package/dist/deploy/build.d.ts.map +1 -1
  18. package/dist/deploy/build.js +91 -2
  19. package/dist/deploy/build.js.map +1 -1
  20. package/dist/event-bus/bus.d.ts.map +1 -1
  21. package/dist/event-bus/bus.js +8 -6
  22. package/dist/event-bus/bus.js.map +1 -1
  23. package/dist/event-bus/registry.d.ts.map +1 -1
  24. package/dist/event-bus/registry.js +10 -6
  25. package/dist/event-bus/registry.js.map +1 -1
  26. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  27. package/dist/file-upload/registry.d.ts.map +1 -1
  28. package/dist/file-upload/registry.js +28 -8
  29. package/dist/file-upload/registry.js.map +1 -1
  30. package/dist/notifications/registry.d.ts.map +1 -1
  31. package/dist/notifications/registry.js +6 -5
  32. package/dist/notifications/registry.js.map +1 -1
  33. package/dist/observability/routes.d.ts +5 -5
  34. package/dist/private-blob/registry.d.ts.map +1 -1
  35. package/dist/private-blob/registry.js +18 -13
  36. package/dist/private-blob/registry.js.map +1 -1
  37. package/dist/progress/routes.d.ts +1 -1
  38. package/dist/resources/handlers.d.ts +2 -2
  39. package/dist/secrets/register.d.ts.map +1 -1
  40. package/dist/secrets/register.js +11 -7
  41. package/dist/secrets/register.js.map +1 -1
  42. package/dist/secrets/routes.d.ts +9 -9
  43. package/dist/shared/global-scope.d.ts +55 -0
  44. package/dist/shared/global-scope.d.ts.map +1 -0
  45. package/dist/shared/global-scope.js +70 -0
  46. package/dist/shared/global-scope.js.map +1 -0
  47. package/dist/sharing/registry.d.ts.map +1 -1
  48. package/dist/sharing/registry.js +2 -16
  49. package/dist/sharing/registry.js.map +1 -1
  50. package/dist/tracking/providers.d.ts.map +1 -1
  51. package/dist/tracking/providers.js +11 -9
  52. package/dist/tracking/providers.js.map +1 -1
  53. package/dist/tracking/registry.d.ts.map +1 -1
  54. package/dist/tracking/registry.js +5 -5
  55. package/dist/tracking/registry.js.map +1 -1
  56. package/package.json +2 -1
@@ -1 +1 @@
1
- {"version":3,"file":"bus.d.ts","sourceRoot":"","sources":["../../src/event-bus/bus.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,KAAK,OAAO,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAwB3E,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAYjE;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAO/C;AAED,wBAAgB,IAAI,CAClB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,GACxB,IAAI,CAuDN;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,CAAC,EAAE,MAAM,GACb;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAQjC;AAED,6CAA6C;AAC7C,wBAAgB,eAAe,IAAI,IAAI,CAItC"}
1
+ {"version":3,"file":"bus.d.ts","sourceRoot":"","sources":["../../src/event-bus/bus.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,KAAK,OAAO,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAqB3E,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAYjE;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAO/C;AAED,wBAAgB,IAAI,CAClB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,GACxB,IAAI,CAuDN;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,CAAC,EAAE,MAAM,GACb;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAQjC;AAED,6CAA6C;AAC7C,wBAAgB,eAAe,IAAI,IAAI,CAItC"}
@@ -7,18 +7,20 @@
7
7
  */
8
8
  import { randomUUID } from "node:crypto";
9
9
  import { EventEmitter } from "node:events";
10
+ import { getScopedGlobal } from "../shared/global-scope.js";
10
11
  import { getEvent } from "./registry.js";
11
- const BUS_KEY = Symbol.for("@agent-native/core/event-bus.bus");
12
+ // globalThis-pinned so one app's ESM graphs share one bus, but scope-aware +
13
+ // lazily resolved so unified workspace deployments (all apps in one isolate)
14
+ // keep per-app buses — one app's handlers never fire for a same-named event
15
+ // emitted by a sibling app. See shared/global-scope.
12
16
  function getBus() {
13
- const g = globalThis;
14
- if (!g[BUS_KEY]) {
17
+ return getScopedGlobal("agent-native.event-bus.bus", () => {
15
18
  const emitter = new EventEmitter();
16
19
  // Many integrations may subscribe to the same event; lift the warning
17
20
  // ceiling rather than printing MaxListenersExceededWarning at runtime.
18
21
  emitter.setMaxListeners(0);
19
- g[BUS_KEY] = { emitter, subscriptions: new Map() };
20
- }
21
- return g[BUS_KEY];
22
+ return { emitter, subscriptions: new Map() };
23
+ });
22
24
  }
23
25
  export function subscribe(event, handler) {
24
26
  if (typeof event !== "string" || !event) {
@@ -1 +1 @@
1
- {"version":3,"file":"bus.js","sourceRoot":"","sources":["../../src/event-bus/bus.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAUzC,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;AAK/D,SAAS,MAAM;IACb,MAAM,CAAC,GAAG,UAAsC,CAAC;IACjD,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QAChB,MAAM,OAAO,GAAG,IAAI,YAAY,EAAE,CAAC;QACnC,sEAAsE;QACtE,uEAAuE;QACvE,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC3B,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;IACrD,CAAC;IACD,OAAO,CAAC,CAAC,OAAO,CAAE,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAa,EAAE,OAAgB;IACvD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IACD,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;IACxB,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IAC9C,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC/B,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,MAAM,GAAG,GAAG,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACtC,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IACvB,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACxC,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC7B,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,IAAI,CAClB,KAAa,EACb,OAAgB,EAChB,IAAyB;IAEzB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAClD,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE5B,IAAI,SAAS,GAAY,OAAO,CAAC;IACjC,IAAI,GAAG,EAAE,CAAC;QACR,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChE,IAAI,MAAM,YAAY,OAAO,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CACV,mCAAmC,KAAK,yBAAyB;gBAC/D,qEAAqE,CACxE,CAAC;QACJ,CAAC;aAAM,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YACzB,OAAO,CAAC,IAAI,CACV,8CAA8C,KAAK,IAAI,EACvD,MAAM,CAAC,MAAM,CACd,CAAC;YACF,OAAO;QACT,CAAC;aAAM,CAAC;YACN,SAAS,GAAI,MAA6B,CAAC,KAAK,CAAC;QACnD,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,IAAI,CACV,4CAA4C,KAAK,KAAK;YACpD,qCAAqC,CACxC,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAc;QAC1B,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,UAAU,EAAE;QACtC,SAAS,EAAE,IAAI,EAAE,SAAS,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACtD,KAAK,EAAE,IAAI,EAAE,KAAK;KACnB,CAAC;IAEF,sEAAsE;IACtE,0CAA0C;IAC1C,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAc,CAAC;IAC5D,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,CAAC,GAAG,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YACxC,IAAI,CAAC,IAAI,OAAQ,CAAmB,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;gBACzD,CAAmB,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;oBACjC,OAAO,CAAC,KAAK,CACX,kCAAkC,KAAK,aAAa,EACpD,GAAG,CACJ,CAAC;gBACJ,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,4BAA4B,KAAK,UAAU,EAAE,GAAG,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,KAAc;IAEd,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,MAAM,GAAG,GAAoC,EAAE,CAAC;IAChD,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC,aAAa,EAAE,CAAC;QAC1C,IAAI,KAAK,IAAI,GAAG,CAAC,KAAK,KAAK,KAAK;YAAE,SAAS;QAC3C,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,6CAA6C;AAC7C,MAAM,UAAU,eAAe;IAC7B,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,GAAG,CAAC,OAAO,CAAC,kBAAkB,EAAE,CAAC;IACjC,GAAG,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;AAC5B,CAAC","sourcesContent":["/**\r\n * Typed pub/sub bus for framework events.\r\n *\r\n * Wraps Node's EventEmitter with payload validation against the registered\r\n * Standard Schema for each event. Handler errors are caught and logged so a\r\n * misbehaving subscriber can never crash the emitter.\r\n */\r\n\r\nimport { randomUUID } from \"node:crypto\";\r\nimport { EventEmitter } from \"node:events\";\r\n\r\nimport { getEvent } from \"./registry.js\";\r\nimport type { EventMeta } from \"./types.js\";\r\n\r\ntype Handler = (payload: unknown, meta: EventMeta) => void | Promise<void>;\r\n\r\ninterface BusState {\r\n emitter: EventEmitter;\r\n subscriptions: Map<string, { event: string; handler: Handler }>;\r\n}\r\n\r\nconst BUS_KEY = Symbol.for(\"@agent-native/core/event-bus.bus\");\r\ninterface GlobalWithBus {\r\n [BUS_KEY]?: BusState;\r\n}\r\n\r\nfunction getBus(): BusState {\r\n const g = globalThis as unknown as GlobalWithBus;\r\n if (!g[BUS_KEY]) {\r\n const emitter = new EventEmitter();\r\n // Many integrations may subscribe to the same event; lift the warning\r\n // ceiling rather than printing MaxListenersExceededWarning at runtime.\r\n emitter.setMaxListeners(0);\r\n g[BUS_KEY] = { emitter, subscriptions: new Map() };\r\n }\r\n return g[BUS_KEY]!;\r\n}\r\n\r\nexport function subscribe(event: string, handler: Handler): string {\r\n if (typeof event !== \"string\" || !event) {\r\n throw new Error(\"subscribe: event name is required\");\r\n }\r\n if (typeof handler !== \"function\") {\r\n throw new Error(\"subscribe: handler must be a function\");\r\n }\r\n const bus = getBus();\r\n const id = randomUUID();\r\n bus.subscriptions.set(id, { event, handler });\r\n bus.emitter.on(event, handler);\r\n return id;\r\n}\r\n\r\nexport function unsubscribe(id: string): boolean {\r\n const bus = getBus();\r\n const sub = bus.subscriptions.get(id);\r\n if (!sub) return false;\r\n bus.emitter.off(sub.event, sub.handler);\r\n bus.subscriptions.delete(id);\r\n return true;\r\n}\r\n\r\nexport function emit(\r\n event: string,\r\n payload: unknown,\r\n meta?: Partial<EventMeta>,\r\n): void {\r\n if (typeof event !== \"string\" || !event) {\r\n throw new Error(\"emit: event name is required\");\r\n }\r\n const bus = getBus();\r\n const def = getEvent(event);\r\n\r\n let validated: unknown = payload;\r\n if (def) {\r\n const result = def.payloadSchema[\"~standard\"].validate(payload);\r\n if (result instanceof Promise) {\r\n console.warn(\r\n `[event-bus] Payload schema for \"${event}\" returned a Promise — ` +\r\n `async validation is not supported. Dispatching unvalidated payload.`,\r\n );\r\n } else if (result.issues) {\r\n console.warn(\r\n `[event-bus] Payload validation failed for \"${event}\":`,\r\n result.issues,\r\n );\r\n return;\r\n } else {\r\n validated = (result as { value: unknown }).value;\r\n }\r\n } else {\r\n console.warn(\r\n `[event-bus] Emitting unregistered event \"${event}\". ` +\r\n `Call registerEvent() to declare it.`,\r\n );\r\n }\r\n\r\n const fullMeta: EventMeta = {\r\n eventId: meta?.eventId ?? randomUUID(),\r\n emittedAt: meta?.emittedAt ?? new Date().toISOString(),\r\n owner: meta?.owner,\r\n };\r\n\r\n // Snapshot listeners so a handler that subscribes/unsubscribes during\r\n // dispatch doesn't perturb this emission.\r\n const listeners = bus.emitter.listeners(event) as Handler[];\r\n for (const listener of listeners) {\r\n try {\r\n const r = listener(validated, fullMeta);\r\n if (r && typeof (r as Promise<void>).catch === \"function\") {\r\n (r as Promise<void>).catch((err) => {\r\n console.error(\r\n `[event-bus] Async handler for \"${event}\" rejected:`,\r\n err,\r\n );\r\n });\r\n }\r\n } catch (err) {\r\n console.error(`[event-bus] Handler for \"${event}\" threw:`, err);\r\n }\r\n }\r\n}\r\n\r\nexport function listSubscriptions(\r\n event?: string,\r\n): { id: string; event: string }[] {\r\n const bus = getBus();\r\n const out: { id: string; event: string }[] = [];\r\n for (const [id, sub] of bus.subscriptions) {\r\n if (event && sub.event !== event) continue;\r\n out.push({ id, event: sub.event });\r\n }\r\n return out;\r\n}\r\n\r\n/** Test helper — drops all subscriptions. */\r\nexport function __resetEventBus(): void {\r\n const bus = getBus();\r\n bus.emitter.removeAllListeners();\r\n bus.subscriptions.clear();\r\n}\r\n"]}
1
+ {"version":3,"file":"bus.js","sourceRoot":"","sources":["../../src/event-bus/bus.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Typed pub/sub bus for framework events.\n *\n * Wraps Node's EventEmitter with payload validation against the registered\n * Standard Schema for each event. Handler errors are caught and logged so a\n * misbehaving subscriber can never crash the emitter.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { EventEmitter } from \"node:events\";\n\nimport { getScopedGlobal } from \"../shared/global-scope.js\";\nimport { getEvent } from \"./registry.js\";\nimport type { EventMeta } from \"./types.js\";\n\ntype Handler = (payload: unknown, meta: EventMeta) => void | Promise<void>;\n\ninterface BusState {\n emitter: EventEmitter;\n subscriptions: Map<string, { event: string; handler: Handler }>;\n}\n\n// globalThis-pinned so one app's ESM graphs share one bus, but scope-aware +\n// lazily resolved so unified workspace deployments (all apps in one isolate)\n// keep per-app buses — one app's handlers never fire for a same-named event\n// emitted by a sibling app. See shared/global-scope.\nfunction getBus(): BusState {\n return getScopedGlobal(\"agent-native.event-bus.bus\", () => {\n const emitter = new EventEmitter();\n // Many integrations may subscribe to the same event; lift the warning\n // ceiling rather than printing MaxListenersExceededWarning at runtime.\n emitter.setMaxListeners(0);\n return { emitter, subscriptions: new Map() };\n });\n}\n\nexport function subscribe(event: string, handler: Handler): string {\n if (typeof event !== \"string\" || !event) {\n throw new Error(\"subscribe: event name is required\");\n }\n if (typeof handler !== \"function\") {\n throw new Error(\"subscribe: handler must be a function\");\n }\n const bus = getBus();\n const id = randomUUID();\n bus.subscriptions.set(id, { event, handler });\n bus.emitter.on(event, handler);\n return id;\n}\n\nexport function unsubscribe(id: string): boolean {\n const bus = getBus();\n const sub = bus.subscriptions.get(id);\n if (!sub) return false;\n bus.emitter.off(sub.event, sub.handler);\n bus.subscriptions.delete(id);\n return true;\n}\n\nexport function emit(\n event: string,\n payload: unknown,\n meta?: Partial<EventMeta>,\n): void {\n if (typeof event !== \"string\" || !event) {\n throw new Error(\"emit: event name is required\");\n }\n const bus = getBus();\n const def = getEvent(event);\n\n let validated: unknown = payload;\n if (def) {\n const result = def.payloadSchema[\"~standard\"].validate(payload);\n if (result instanceof Promise) {\n console.warn(\n `[event-bus] Payload schema for \"${event}\" returned a Promise — ` +\n `async validation is not supported. Dispatching unvalidated payload.`,\n );\n } else if (result.issues) {\n console.warn(\n `[event-bus] Payload validation failed for \"${event}\":`,\n result.issues,\n );\n return;\n } else {\n validated = (result as { value: unknown }).value;\n }\n } else {\n console.warn(\n `[event-bus] Emitting unregistered event \"${event}\". ` +\n `Call registerEvent() to declare it.`,\n );\n }\n\n const fullMeta: EventMeta = {\n eventId: meta?.eventId ?? randomUUID(),\n emittedAt: meta?.emittedAt ?? new Date().toISOString(),\n owner: meta?.owner,\n };\n\n // Snapshot listeners so a handler that subscribes/unsubscribes during\n // dispatch doesn't perturb this emission.\n const listeners = bus.emitter.listeners(event) as Handler[];\n for (const listener of listeners) {\n try {\n const r = listener(validated, fullMeta);\n if (r && typeof (r as Promise<void>).catch === \"function\") {\n (r as Promise<void>).catch((err) => {\n console.error(\n `[event-bus] Async handler for \"${event}\" rejected:`,\n err,\n );\n });\n }\n } catch (err) {\n console.error(`[event-bus] Handler for \"${event}\" threw:`, err);\n }\n }\n}\n\nexport function listSubscriptions(\n event?: string,\n): { id: string; event: string }[] {\n const bus = getBus();\n const out: { id: string; event: string }[] = [];\n for (const [id, sub] of bus.subscriptions) {\n if (event && sub.event !== event) continue;\n out.push({ id, event: sub.event });\n }\n return out;\n}\n\n/** Test helper — drops all subscriptions. */\nexport function __resetEventBus(): void {\n const bus = getBus();\n bus.emitter.removeAllListeners();\n bus.subscriptions.clear();\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/event-bus/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAYlD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,CAWxD;AAED,0DAA0D;AAC1D,wBAAgB,UAAU,IAAI,eAAe,EAAE,CAE9C;AAED,iDAAiD;AACjD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAElE;AAED,sDAAsD;AACtD,wBAAgB,oBAAoB,IAAI,IAAI,CAG3C"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/event-bus/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAalD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,CAWxD;AAED,0DAA0D;AAC1D,wBAAgB,UAAU,IAAI,eAAe,EAAE,CAE9C;AAED,iDAAiD;AACjD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAElE;AAED,sDAAsD;AACtD,wBAAgB,oBAAoB,IAAI,IAAI,CAG3C"}
@@ -6,10 +6,14 @@
6
6
  * payloads, and the Automations UI lists them so users can build triggers.
7
7
  */
8
8
  import { z } from "zod";
9
+ import { getScopedGlobal } from "../shared/global-scope.js";
9
10
  // Pin to globalThis so multiple ESM graphs (dev-mode Vite + Nitro, symlinks,
10
11
  // dist/ vs src/) share a single registry. Same pattern as secrets/register.ts.
11
- const REGISTRY_KEY = Symbol.for("@agent-native/core/event-bus.registry");
12
- const registry = (globalThis[REGISTRY_KEY] ??= new Map());
12
+ // Scope-aware + lazily resolved so unified workspace deployments (all apps in
13
+ // one isolate) keep per-app event definitions. See shared/global-scope.
14
+ function getEventRegistry() {
15
+ return getScopedGlobal("agent-native.event-bus.registry", () => new Map());
16
+ }
13
17
  /**
14
18
  * Register (or replace) an event definition.
15
19
  *
@@ -26,19 +30,19 @@ export function registerEvent(def) {
26
30
  if (!def.payloadSchema) {
27
31
  throw new Error("registerEvent: def.payloadSchema is required");
28
32
  }
29
- registry.set(def.name, def);
33
+ getEventRegistry().set(def.name, def);
30
34
  }
31
35
  /** Return all registered events in registration order. */
32
36
  export function listEvents() {
33
- return Array.from(registry.values());
37
+ return Array.from(getEventRegistry().values());
34
38
  }
35
39
  /** Look up a single registered event by name. */
36
40
  export function getEvent(name) {
37
- return registry.get(name);
41
+ return getEventRegistry().get(name);
38
42
  }
39
43
  /** Test helper — clears the registry between runs. */
40
44
  export function __resetEventRegistry() {
41
- registry.clear();
45
+ getEventRegistry().clear();
42
46
  registerBuiltInEvents();
43
47
  }
44
48
  function registerBuiltInEvents() {
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/event-bus/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,6EAA6E;AAC7E,+EAA+E;AAC/E,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;AAIzE,MAAM,QAAQ,GAAiC,CAC7C,UACD,CAAC,YAAY,CAAC,KAAK,IAAI,GAAG,EAAE,CAAC,CAAC;AAE/B;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,GAAoB;IAChD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IACD,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAC9B,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,UAAU;IACxB,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;AACvC,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,oBAAoB;IAClC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACjB,qBAAqB,EAAE,CAAC;AAC1B,CAAC;AAED,SAAS,qBAAqB;IAC5B,aAAa,CAAC;QACZ,IAAI,EAAE,kBAAkB;QACxB,WAAW,EACT,6FAA6F;QAC/F,aAAa,EAAE,CAAC;aACb,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC;aAC9D,QAAQ,EAAiD;KAC7D,CAAC,CAAC;IAEH,aAAa,CAAC;QACZ,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE,wDAAwD;QACrE,aAAa,EAAE,CAAC,CAAC,MAAM,CAAC;YACtB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAC/B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAChC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;SAC7B,CAAgD;KAClD,CAAC,CAAC;AACL,CAAC;AAED,qBAAqB,EAAE,CAAC","sourcesContent":["/**\r\n * In-process registry of event definitions.\r\n *\r\n * Integrations and templates call `registerEvent()` at module load to declare\r\n * the event types they emit. The bus uses these definitions to validate\r\n * payloads, and the Automations UI lists them so users can build triggers.\r\n */\r\n\r\nimport { z } from \"zod\";\r\n\r\nimport type { EventDefinition } from \"./types.js\";\r\n\r\n// Pin to globalThis so multiple ESM graphs (dev-mode Vite + Nitro, symlinks,\r\n// dist/ vs src/) share a single registry. Same pattern as secrets/register.ts.\r\nconst REGISTRY_KEY = Symbol.for(\"@agent-native/core/event-bus.registry\");\r\ninterface GlobalWithRegistry {\r\n [REGISTRY_KEY]?: Map<string, EventDefinition>;\r\n}\r\nconst registry: Map<string, EventDefinition> = ((\r\n globalThis as unknown as GlobalWithRegistry\r\n)[REGISTRY_KEY] ??= new Map());\r\n\r\n/**\r\n * Register (or replace) an event definition.\r\n *\r\n * Subsequent registrations with the same `name` replace the previous\r\n * definition — later plugins can override built-in defaults.\r\n */\r\nexport function registerEvent(def: EventDefinition): void {\r\n if (!def || typeof def.name !== \"string\" || !def.name) {\r\n throw new Error(\"registerEvent: def.name is required\");\r\n }\r\n if (typeof def.description !== \"string\" || !def.description) {\r\n throw new Error(\"registerEvent: def.description is required\");\r\n }\r\n if (!def.payloadSchema) {\r\n throw new Error(\"registerEvent: def.payloadSchema is required\");\r\n }\r\n registry.set(def.name, def);\r\n}\r\n\r\n/** Return all registered events in registration order. */\r\nexport function listEvents(): EventDefinition[] {\r\n return Array.from(registry.values());\r\n}\r\n\r\n/** Look up a single registered event by name. */\r\nexport function getEvent(name: string): EventDefinition | undefined {\r\n return registry.get(name);\r\n}\r\n\r\n/** Test helper — clears the registry between runs. */\r\nexport function __resetEventRegistry(): void {\r\n registry.clear();\r\n registerBuiltInEvents();\r\n}\r\n\r\nfunction registerBuiltInEvents(): void {\r\n registerEvent({\r\n name: \"test.event.fired\",\r\n description:\r\n \"Developer test event — fired manually from the Automations UI or via the test-event action.\",\r\n payloadSchema: z\r\n .object({ data: z.record(z.string(), z.unknown()).optional() })\r\n .optional() as unknown as EventDefinition[\"payloadSchema\"],\r\n });\r\n\r\n registerEvent({\r\n name: \"agent.turn.completed\",\r\n description: \"Fires after the agent completes a conversational turn.\",\r\n payloadSchema: z.object({\r\n threadId: z.string().optional(),\r\n turnIndex: z.number().optional(),\r\n model: z.string().optional(),\r\n }) as unknown as EventDefinition[\"payloadSchema\"],\r\n });\r\n}\r\n\r\nregisterBuiltInEvents();\r\n"]}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/event-bus/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAG5D,6EAA6E;AAC7E,+EAA+E;AAC/E,8EAA8E;AAC9E,wEAAwE;AACxE,SAAS,gBAAgB;IACvB,OAAO,eAAe,CACpB,iCAAiC,EACjC,GAAG,EAAE,CAAC,IAAI,GAAG,EAA2B,CACzC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,GAAoB;IAChD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IACD,gBAAgB,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AACxC,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,UAAU;IACxB,OAAO,KAAK,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;AACjD,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,OAAO,gBAAgB,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,oBAAoB;IAClC,gBAAgB,EAAE,CAAC,KAAK,EAAE,CAAC;IAC3B,qBAAqB,EAAE,CAAC;AAC1B,CAAC;AAED,SAAS,qBAAqB;IAC5B,aAAa,CAAC;QACZ,IAAI,EAAE,kBAAkB;QACxB,WAAW,EACT,6FAA6F;QAC/F,aAAa,EAAE,CAAC;aACb,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC;aAC9D,QAAQ,EAAiD;KAC7D,CAAC,CAAC;IAEH,aAAa,CAAC;QACZ,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE,wDAAwD;QACrE,aAAa,EAAE,CAAC,CAAC,MAAM,CAAC;YACtB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAC/B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAChC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;SAC7B,CAAgD;KAClD,CAAC,CAAC;AACL,CAAC;AAED,qBAAqB,EAAE,CAAC","sourcesContent":["/**\n * In-process registry of event definitions.\n *\n * Integrations and templates call `registerEvent()` at module load to declare\n * the event types they emit. The bus uses these definitions to validate\n * payloads, and the Automations UI lists them so users can build triggers.\n */\n\nimport { z } from \"zod\";\n\nimport { getScopedGlobal } from \"../shared/global-scope.js\";\nimport type { EventDefinition } from \"./types.js\";\n\n// Pin to globalThis so multiple ESM graphs (dev-mode Vite + Nitro, symlinks,\n// dist/ vs src/) share a single registry. Same pattern as secrets/register.ts.\n// Scope-aware + lazily resolved so unified workspace deployments (all apps in\n// one isolate) keep per-app event definitions. See shared/global-scope.\nfunction getEventRegistry(): Map<string, EventDefinition> {\n return getScopedGlobal(\n \"agent-native.event-bus.registry\",\n () => new Map<string, EventDefinition>(),\n );\n}\n\n/**\n * Register (or replace) an event definition.\n *\n * Subsequent registrations with the same `name` replace the previous\n * definition — later plugins can override built-in defaults.\n */\nexport function registerEvent(def: EventDefinition): void {\n if (!def || typeof def.name !== \"string\" || !def.name) {\n throw new Error(\"registerEvent: def.name is required\");\n }\n if (typeof def.description !== \"string\" || !def.description) {\n throw new Error(\"registerEvent: def.description is required\");\n }\n if (!def.payloadSchema) {\n throw new Error(\"registerEvent: def.payloadSchema is required\");\n }\n getEventRegistry().set(def.name, def);\n}\n\n/** Return all registered events in registration order. */\nexport function listEvents(): EventDefinition[] {\n return Array.from(getEventRegistry().values());\n}\n\n/** Look up a single registered event by name. */\nexport function getEvent(name: string): EventDefinition | undefined {\n return getEventRegistry().get(name);\n}\n\n/** Test helper — clears the registry between runs. */\nexport function __resetEventRegistry(): void {\n getEventRegistry().clear();\n registerBuiltInEvents();\n}\n\nfunction registerBuiltInEvents(): void {\n registerEvent({\n name: \"test.event.fired\",\n description:\n \"Developer test event — fired manually from the Automations UI or via the test-event action.\",\n payloadSchema: z\n .object({ data: z.record(z.string(), z.unknown()).optional() })\n .optional() as unknown as EventDefinition[\"payloadSchema\"],\n });\n\n registerEvent({\n name: \"agent.turn.completed\",\n description: \"Fires after the agent completes a conversational turn.\",\n payloadSchema: z.object({\n threadId: z.string().optional(),\n turnIndex: z.number().optional(),\n model: z.string().optional(),\n }) as unknown as EventDefinition[\"payloadSchema\"],\n });\n}\n\nregisterBuiltInEvents();\n"]}
@@ -17,12 +17,12 @@ declare const _default: import("../../action.js").ActionDefinition<{
17
17
  id?: undefined;
18
18
  provider?: undefined;
19
19
  } | {
20
+ error?: undefined;
20
21
  configured?: undefined;
21
22
  connectPath?: undefined;
22
23
  url: string;
23
24
  id: string;
24
25
  provider: string;
25
- error?: undefined;
26
26
  }>;
27
27
  export default _default;
28
28
  //# sourceMappingURL=upload-image.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/file-upload/registry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAmBpB;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAE7E;AAED,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE7D;AAED,wBAAgB,uBAAuB,IAAI,kBAAkB,EAAE,CAE9D;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,IAAI,kBAAkB,GAAG,IAAI,CAQvE;AAED,wBAAsB,qCAAqC,IAAI,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAgBhG;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA0ClC"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/file-upload/registry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AA2BpB;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAE7E;AAED,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE7D;AAED,wBAAgB,uBAAuB,IAAI,kBAAkB,EAAE,CAE9D;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,IAAI,kBAAkB,GAAG,IAAI,CAQvE;AAED,wBAAsB,qCAAqC,IAAI,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAgBhG;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA2ClC"}
@@ -1,19 +1,38 @@
1
+ import { getScopedGlobal } from "../shared/global-scope.js";
1
2
  import { builderFileUploadProvider } from "./builder.js";
2
- const globals = globalThis;
3
- const providers = (globals.__agentNativeFileUploadProviders ??= new Map());
4
- const warnedFallbackRef = (globals.__agentNativeFileUploadWarnedFallback ??= { value: false });
3
+ // Why globalThis: in dev (Vite HMR) and in some Nitro/Rollup bundle splits,
4
+ // this module can be evaluated more than once — the plugin file that
5
+ // registers a provider lands in one module instance and the request handler
6
+ // that reads providers lands in another, so the call site sees an empty map
7
+ // even though `registerFileUploadProvider` succeeded. Pinning the singletons
8
+ // on `globalThis` guarantees one set of providers per Node process,
9
+ // independent of how the bundler split the chunks.
10
+ //
11
+ // Why scope-aware + lazy: on a unified workspace deployment every app shares
12
+ // one isolate, so a raw globalThis pin would merge every app's providers into
13
+ // one map (an /assets upload was served by clips' provider). The keys are
14
+ // namespaced per app via `getScopedGlobal`, resolved lazily on every access
15
+ // so the scope id set by the generated worker entry is always honored.
16
+ function getProviders() {
17
+ return getScopedGlobal("agent-native.file-upload.providers", () => new Map());
18
+ }
19
+ function getWarnedFallbackRef() {
20
+ return getScopedGlobal("agent-native.file-upload.warned-fallback", () => ({
21
+ value: false,
22
+ }));
23
+ }
5
24
  /**
6
25
  * Register a file upload provider. Call from a server plugin or app
7
26
  * bootstrap. Idempotent per id — later calls with the same id replace.
8
27
  */
9
28
  export function registerFileUploadProvider(provider) {
10
- providers.set(provider.id, provider);
29
+ getProviders().set(provider.id, provider);
11
30
  }
12
31
  export function unregisterFileUploadProvider(id) {
13
- providers.delete(id);
32
+ getProviders().delete(id);
14
33
  }
15
34
  export function listFileUploadProviders() {
16
- return [...providers.values()];
35
+ return [...getProviders().values()];
17
36
  }
18
37
  /**
19
38
  * Returns the first configured provider, checking user-registered ones first
@@ -22,7 +41,7 @@ export function listFileUploadProviders() {
22
41
  * SQL fallback.
23
42
  */
24
43
  export function getActiveFileUploadProvider() {
25
- for (const provider of providers.values()) {
44
+ for (const provider of getProviders().values()) {
26
45
  if (provider.isConfigured())
27
46
  return provider;
28
47
  }
@@ -32,7 +51,7 @@ export function getActiveFileUploadProvider() {
32
51
  return null;
33
52
  }
34
53
  export async function getActiveFileUploadProviderForRequest() {
35
- for (const provider of providers.values()) {
54
+ for (const provider of getProviders().values()) {
36
55
  if (provider.isConfigured())
37
56
  return provider;
38
57
  if (provider.isConfiguredForRequest) {
@@ -84,6 +103,7 @@ export async function uploadFile(input) {
84
103
  // API, rate-limit) propagate to the caller; do NOT catch them here.
85
104
  return await builderFileUploadProvider.upload(input);
86
105
  }
106
+ const warnedFallbackRef = getWarnedFallbackRef();
87
107
  if (!warnedFallbackRef.value) {
88
108
  warnedFallbackRef.value = true;
89
109
  console.warn("[agent-native] No file upload provider configured. " +
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/file-upload/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { builderFileUploadProvider } from \"./builder.js\";\r\nimport type {\r\n FileUploadInput,\r\n FileUploadProvider,\r\n FileUploadResult,\r\n} from \"./types.js\";\r\n\r\n// Why globalThis: in dev (Vite HMR) and in some Nitro/Rollup bundle splits,\r\n// this module can be evaluated more than once — the plugin file that\r\n// registers a provider lands in one module instance and the request handler\r\n// that reads providers lands in another, so the call site sees an empty map\r\n// even though `registerFileUploadProvider` succeeded. Pinning the singletons\r\n// on `globalThis` guarantees one set of providers per Node process,\r\n// independent of how the bundler split the chunks.\r\ninterface FileUploadGlobals {\r\n __agentNativeFileUploadProviders?: Map<string, FileUploadProvider>;\r\n __agentNativeFileUploadWarnedFallback?: { value: boolean };\r\n}\r\nconst globals = globalThis as typeof globalThis & FileUploadGlobals;\r\nconst providers: Map<string, FileUploadProvider> =\r\n (globals.__agentNativeFileUploadProviders ??= new Map());\r\nconst warnedFallbackRef: { value: boolean } =\r\n (globals.__agentNativeFileUploadWarnedFallback ??= { value: false });\r\n\r\n/**\r\n * Register a file upload provider. Call from a server plugin or app\r\n * bootstrap. Idempotent per id — later calls with the same id replace.\r\n */\r\nexport function registerFileUploadProvider(provider: FileUploadProvider): void {\r\n providers.set(provider.id, provider);\r\n}\r\n\r\nexport function unregisterFileUploadProvider(id: string): void {\r\n providers.delete(id);\r\n}\r\n\r\nexport function listFileUploadProviders(): FileUploadProvider[] {\r\n return [...providers.values()];\r\n}\r\n\r\n/**\r\n * Returns the first configured provider, checking user-registered ones first\r\n * and falling back to the built-in Builder.io provider when its env is set.\r\n * Returns `null` when nothing is configured — callers should then use the\r\n * SQL fallback.\r\n */\r\nexport function getActiveFileUploadProvider(): FileUploadProvider | null {\r\n for (const provider of providers.values()) {\r\n if (provider.isConfigured()) return provider;\r\n }\r\n if (builderFileUploadProvider.isConfigured()) {\r\n return builderFileUploadProvider;\r\n }\r\n return null;\r\n}\r\n\r\nexport async function getActiveFileUploadProviderForRequest(): Promise<FileUploadProvider | null> {\r\n for (const provider of providers.values()) {\r\n if (provider.isConfigured()) return provider;\r\n if (provider.isConfiguredForRequest) {\r\n try {\r\n if (await provider.isConfiguredForRequest()) return provider;\r\n } catch {\r\n // Treat failed scoped credential lookups as unavailable. The upload\r\n // call will surface real provider errors after a provider is selected.\r\n }\r\n }\r\n }\r\n if (builderFileUploadProvider.isConfigured()) {\r\n return builderFileUploadProvider;\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Upload a file via the active provider, or `null` if no provider is\r\n * configured. Callers use `null` as the signal to fall back to SQL\r\n * storage. On the first fallback we log a one-time warning because\r\n * storing files in SQL is not optimal for production.\r\n */\r\nexport async function uploadFile(\r\n input: FileUploadInput,\r\n): Promise<FileUploadResult | null> {\r\n const provider = await getActiveFileUploadProviderForRequest();\r\n // User-registered providers (S3, etc.) may be configured by sync runtime\r\n // state or request-scoped DB secrets. Builder still gets an explicit async\r\n // credential check below because its sync isConfigured() only checks env.\r\n if (provider && provider !== builderFileUploadProvider) {\r\n return provider.upload(input);\r\n }\r\n\r\n // Resolve credentials asynchronously (works when request context is set\r\n // via runWithRequestContext — actions always have one via action-routes.ts).\r\n // Two separate try-catch blocks ensure a real upload failure is never\r\n // silently swallowed as a \"no credentials\" case.\r\n let builderKey: string | null = null;\r\n try {\r\n const { resolveBuilderPrivateKey } =\r\n await import(\"../server/credential-provider.js\");\r\n builderKey = await resolveBuilderPrivateKey();\r\n } catch (err) {\r\n // DB unavailable or credential store not ready — can't resolve key.\r\n // Log and fall through to the SQL fallback below.\r\n console.warn(\r\n \"[agent-native] Builder credential check failed:\",\r\n err instanceof Error ? err.message : String(err),\r\n );\r\n }\r\n\r\n if (builderKey) {\r\n // Credentials confirmed — attempt the upload. Real errors (network,\r\n // API, rate-limit) propagate to the caller; do NOT catch them here.\r\n return await builderFileUploadProvider.upload(input);\r\n }\r\n\r\n if (!warnedFallbackRef.value) {\r\n warnedFallbackRef.value = true;\r\n console.warn(\r\n \"[agent-native] No file upload provider configured. \" +\r\n \"Connect or reconnect Builder.io in Settings → File uploads, \" +\r\n \"or register a custom provider (S3, R2, GCS, …) via registerFileUploadProvider().\",\r\n );\r\n }\r\n return null;\r\n}\r\n"]}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/file-upload/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { getScopedGlobal } from \"../shared/global-scope.js\";\nimport { builderFileUploadProvider } from \"./builder.js\";\nimport type {\n FileUploadInput,\n FileUploadProvider,\n FileUploadResult,\n} from \"./types.js\";\n\n// Why globalThis: in dev (Vite HMR) and in some Nitro/Rollup bundle splits,\n// this module can be evaluated more than once — the plugin file that\n// registers a provider lands in one module instance and the request handler\n// that reads providers lands in another, so the call site sees an empty map\n// even though `registerFileUploadProvider` succeeded. Pinning the singletons\n// on `globalThis` guarantees one set of providers per Node process,\n// independent of how the bundler split the chunks.\n//\n// Why scope-aware + lazy: on a unified workspace deployment every app shares\n// one isolate, so a raw globalThis pin would merge every app's providers into\n// one map (an /assets upload was served by clips' provider). The keys are\n// namespaced per app via `getScopedGlobal`, resolved lazily on every access\n// so the scope id set by the generated worker entry is always honored.\nfunction getProviders(): Map<string, FileUploadProvider> {\n return getScopedGlobal(\n \"agent-native.file-upload.providers\",\n () => new Map<string, FileUploadProvider>(),\n );\n}\nfunction getWarnedFallbackRef(): { value: boolean } {\n return getScopedGlobal(\"agent-native.file-upload.warned-fallback\", () => ({\n value: false,\n }));\n}\n\n/**\n * Register a file upload provider. Call from a server plugin or app\n * bootstrap. Idempotent per id — later calls with the same id replace.\n */\nexport function registerFileUploadProvider(provider: FileUploadProvider): void {\n getProviders().set(provider.id, provider);\n}\n\nexport function unregisterFileUploadProvider(id: string): void {\n getProviders().delete(id);\n}\n\nexport function listFileUploadProviders(): FileUploadProvider[] {\n return [...getProviders().values()];\n}\n\n/**\n * Returns the first configured provider, checking user-registered ones first\n * and falling back to the built-in Builder.io provider when its env is set.\n * Returns `null` when nothing is configured — callers should then use the\n * SQL fallback.\n */\nexport function getActiveFileUploadProvider(): FileUploadProvider | null {\n for (const provider of getProviders().values()) {\n if (provider.isConfigured()) return provider;\n }\n if (builderFileUploadProvider.isConfigured()) {\n return builderFileUploadProvider;\n }\n return null;\n}\n\nexport async function getActiveFileUploadProviderForRequest(): Promise<FileUploadProvider | null> {\n for (const provider of getProviders().values()) {\n if (provider.isConfigured()) return provider;\n if (provider.isConfiguredForRequest) {\n try {\n if (await provider.isConfiguredForRequest()) return provider;\n } catch {\n // Treat failed scoped credential lookups as unavailable. The upload\n // call will surface real provider errors after a provider is selected.\n }\n }\n }\n if (builderFileUploadProvider.isConfigured()) {\n return builderFileUploadProvider;\n }\n return null;\n}\n\n/**\n * Upload a file via the active provider, or `null` if no provider is\n * configured. Callers use `null` as the signal to fall back to SQL\n * storage. On the first fallback we log a one-time warning because\n * storing files in SQL is not optimal for production.\n */\nexport async function uploadFile(\n input: FileUploadInput,\n): Promise<FileUploadResult | null> {\n const provider = await getActiveFileUploadProviderForRequest();\n // User-registered providers (S3, etc.) may be configured by sync runtime\n // state or request-scoped DB secrets. Builder still gets an explicit async\n // credential check below because its sync isConfigured() only checks env.\n if (provider && provider !== builderFileUploadProvider) {\n return provider.upload(input);\n }\n\n // Resolve credentials asynchronously (works when request context is set\n // via runWithRequestContext — actions always have one via action-routes.ts).\n // Two separate try-catch blocks ensure a real upload failure is never\n // silently swallowed as a \"no credentials\" case.\n let builderKey: string | null = null;\n try {\n const { resolveBuilderPrivateKey } =\n await import(\"../server/credential-provider.js\");\n builderKey = await resolveBuilderPrivateKey();\n } catch (err) {\n // DB unavailable or credential store not ready — can't resolve key.\n // Log and fall through to the SQL fallback below.\n console.warn(\n \"[agent-native] Builder credential check failed:\",\n err instanceof Error ? err.message : String(err),\n );\n }\n\n if (builderKey) {\n // Credentials confirmed — attempt the upload. Real errors (network,\n // API, rate-limit) propagate to the caller; do NOT catch them here.\n return await builderFileUploadProvider.upload(input);\n }\n\n const warnedFallbackRef = getWarnedFallbackRef();\n if (!warnedFallbackRef.value) {\n warnedFallbackRef.value = true;\n console.warn(\n \"[agent-native] No file upload provider configured. \" +\n \"Connect or reconnect Builder.io in Settings → File uploads, \" +\n \"or register a custom provider (S3, R2, GCS, …) via registerFileUploadProvider().\",\n );\n }\n return null;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/notifications/registry.ts"],"names":[],"mappings":"AAOA,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAClB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,0BAA0B;IACzC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAiCD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,mBAAmB,GAC3B,IAAI,CAUN;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED,wBAAgB,wBAAwB,IAAI,MAAM,EAAE,CAEnD;AAgBD,wBAAsB,MAAM,CAC1B,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAEnC;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,0BAA0B,CAAC,CAuFrC;AAoBD,mDAAmD;AACnD,wBAAgB,2BAA2B,IAAI,IAAI,CAElD;AAED,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,wBAAwB,EACxB,kBAAkB,EAClB,WAAW,GACZ,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/notifications/registry.ts"],"names":[],"mappings":"AAQA,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAClB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,0BAA0B;IACzC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAkCD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,mBAAmB,GAC3B,IAAI,CAUN;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED,wBAAgB,wBAAwB,IAAI,MAAM,EAAE,CAEnD;AAgBD,wBAAsB,MAAM,CAC1B,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAEnC;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,0BAA0B,CAAC,CAuFrC;AAoBD,mDAAmD;AACnD,wBAAgB,2BAA2B,IAAI,IAAI,CAElD;AAED,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,wBAAwB,EACxB,kBAAkB,EAClB,WAAW,GACZ,MAAM,YAAY,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { emit as emitBusEvent } from "../event-bus/bus.js";
3
3
  import { registerEvent } from "../event-bus/registry.js";
4
+ import { getScopedGlobal } from "../shared/global-scope.js";
4
5
  import { truncate } from "../shared/truncate.js";
5
6
  import { insertNotification, updateDeliveredChannels } from "./store.js";
6
7
  import { NOTIFICATION_SEVERITIES, } from "./types.js";
@@ -22,12 +23,12 @@ registerEvent({
22
23
  deliveredChannels: ["inbox", "webhook"],
23
24
  },
24
25
  });
25
- const REGISTRY_KEY = Symbol.for("@agent-native/core/notifications.registry");
26
+ const REGISTRY_BASE_KEY = "agent-native.notifications.registry";
27
+ // globalThis-pinned so one app's ESM graphs share one channel registry, but
28
+ // scope-aware + lazily resolved so unified workspace deployments (all apps in
29
+ // one isolate) keep per-app channels. See shared/global-scope.
26
30
  function getRegistry() {
27
- const g = globalThis;
28
- if (!g[REGISTRY_KEY])
29
- g[REGISTRY_KEY] = new Map();
30
- return g[REGISTRY_KEY];
31
+ return getScopedGlobal(REGISTRY_BASE_KEY, () => new Map());
31
32
  }
32
33
  export function registerNotificationChannel(channel) {
33
34
  if (!channel?.name) {
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/notifications/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,IAAI,IAAI,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EACL,uBAAuB,GAKxB,MAAM,YAAY,CAAC;AAOpB,aAAa,CAAC;IACZ,IAAI,EAAE,mBAAmB;IACzB,WAAW,EACT,mIAAmI;IACrI,aAAa,EAAE,CAAC,CAAC,MAAM,CAAC;QACtB,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QACrC,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC;QACzC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;QACjB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC3B,iBAAiB,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;KACvC,CAAgD;IACjD,OAAO,EAAE;QACP,cAAc,EAAE,SAAS;QACzB,QAAQ,EAAE,UAAU;QACpB,KAAK,EAAE,gBAAgB;QACvB,IAAI,EAAE,2BAA2B;QACjC,iBAAiB,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC;KACxC;CACF,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;AAK7E,SAAS,WAAW;IAClB,MAAM,CAAC,GAAG,UAA2C,CAAC;IACtD,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC;QAAE,CAAC,CAAC,YAAY,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC;IAClD,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,2BAA2B,CACzC,OAA4B;IAE5B,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,UAAU,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CACb,iEAAiE,CAClE,CAAC;IACJ,CAAC;IACD,WAAW,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,IAAY;IACxD,OAAO,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,wBAAwB;IACtC,OAAO,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AAC1C,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,aAAa,GAAG,GAAG,CAAC;AAC1B,MAAM,YAAY,GAAG,IAAI,CAAC;AAE1B,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,KAAwB,EACxB,IAAsB;IAEtB,OAAO,CAAC,MAAM,kBAAkB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC;AAC9D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAAwB,EACxB,IAAsB;IAEtB,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IACD,KAAK,GAAG;QACN,GAAG,KAAK;QACR,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,aAAa,CAAC;QAC3C,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,YAAY,CAAC;KACzC,CAAC;IACF,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,cAAc,GAAG,mBAAmB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAE3D,mEAAmE;IACnE,MAAM,QAAQ,GAAG,CAAC,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACrE,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,IAAI,MAAgC,CAAC;IAErC,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,CAAC;YACH,qEAAqE;YACrE,qEAAqE;YACrE,MAAM,GAAG,MAAM,kBAAkB,CAAC;gBAChC,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,QAAQ,EAAE,cAAc;gBACxB,iBAAiB,EAAE,CAAC,OAAO,CAAC;aAC7B,CAAC,CAAC;YACH,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,GAAG,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,0EAA0E;IAC1E,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CACtC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QAC7B,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACrD,oEAAoE;QACpE,IAAI,SAAS,KAAK,KAAK;YAAE,OAAO,IAAI,CAAC;QACrC,OAAO,OAAO,CAAC,IAAI,CAAC;IACtB,CAAC,CAAC,CACH,CAAC;IACF,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACvB,IAAI,CAAC,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YAC7B,IAAI,CAAC,CAAC,KAAK;gBAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACvC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CACX,4BAA4B,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,WAAW,EACvD,CAAC,CAAC,MAAM,CACT,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,eAAe,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC;IAC7D,IAAI,MAAM,IAAI,eAAe,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,uBAAuB,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;YACpD,MAAM,GAAG,EAAE,GAAG,MAAM,EAAE,iBAAiB,EAAE,SAAS,EAAE,CAAC;QACvD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,kDAAkD,EAAE,GAAG,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IAED,sEAAsE;IACtE,uEAAuE;IACvE,0CAA0C;IAC1C,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,YAAY,CACV,mBAAmB,EACnB;gBACE,cAAc,EAAE,MAAM,EAAE,EAAE;gBAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,iBAAiB,EAAE,SAAS;aAC7B,EACD,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CACtB,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,iBAAiB,EAAE,SAAS,EAAE,CAAC;AAChE,CAAC;AAED,SAAS,mBAAmB,CAC1B,QAA6C;IAE7C,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAC7C,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CACR,GAAG,KAAK,UAAU,IAAI,GAAG,KAAK,YAAY,IAAI,GAAG,KAAK,iBAAiB,CAC1E,CAAC;IACF,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAClE,CAAC;AAED,SAAS,cAAc,CAAC,SAAoB;IAC1C,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1C,IAAI,CAAC,SAAS;QAAE,OAAO,GAAG,CAAC;IAC3B,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AACvD,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,2BAA2B;IACzC,WAAW,EAAE,CAAC,KAAK,EAAE,CAAC;AACxB,CAAC;AAED,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,wBAAwB,EACxB,kBAAkB,EAClB,WAAW,GACZ,MAAM,YAAY,CAAC","sourcesContent":["import { z } from \"zod\";\r\n\r\nimport { emit as emitBusEvent } from \"../event-bus/bus.js\";\r\nimport { registerEvent } from \"../event-bus/registry.js\";\r\nimport type { EventDefinition } from \"../event-bus/types.js\";\r\nimport { truncate } from \"../shared/truncate.js\";\r\nimport { insertNotification, updateDeliveredChannels } from \"./store.js\";\r\nimport {\r\n NOTIFICATION_SEVERITIES,\r\n type NotificationChannel,\r\n type NotificationInput,\r\n type NotificationMeta,\r\n type Notification,\r\n} from \"./types.js\";\r\n\r\nexport interface NotificationDeliveryResult {\r\n notification?: Notification;\r\n deliveredChannels: string[];\r\n}\r\n\r\nregisterEvent({\r\n name: \"notification.sent\",\r\n description:\r\n \"Fires after notify() delivers to at least one channel. Automations can chain off this — e.g. fan critical notifications to Slack.\",\r\n payloadSchema: z.object({\r\n notificationId: z.string().optional(),\r\n severity: z.enum(NOTIFICATION_SEVERITIES),\r\n title: z.string(),\r\n body: z.string().optional(),\r\n deliveredChannels: z.array(z.string()),\r\n }) as unknown as EventDefinition[\"payloadSchema\"],\r\n example: {\r\n notificationId: \"ntf_abc\",\r\n severity: \"critical\",\r\n title: \"Payment failed\",\r\n body: \"Card ending 4242 declined\",\r\n deliveredChannels: [\"inbox\", \"webhook\"],\r\n },\r\n});\r\n\r\nconst REGISTRY_KEY = Symbol.for(\"@agent-native/core/notifications.registry\");\r\ninterface GlobalWithRegistry {\r\n [REGISTRY_KEY]?: Map<string, NotificationChannel>;\r\n}\r\n\r\nfunction getRegistry(): Map<string, NotificationChannel> {\r\n const g = globalThis as unknown as GlobalWithRegistry;\r\n if (!g[REGISTRY_KEY]) g[REGISTRY_KEY] = new Map();\r\n return g[REGISTRY_KEY];\r\n}\r\n\r\nexport function registerNotificationChannel(\r\n channel: NotificationChannel,\r\n): void {\r\n if (!channel?.name) {\r\n throw new Error(\"registerNotificationChannel: channel.name is required\");\r\n }\r\n if (typeof channel.deliver !== \"function\") {\r\n throw new Error(\r\n \"registerNotificationChannel: channel.deliver must be a function\",\r\n );\r\n }\r\n getRegistry().set(channel.name, channel);\r\n}\r\n\r\nexport function unregisterNotificationChannel(name: string): boolean {\r\n return getRegistry().delete(name);\r\n}\r\n\r\nexport function listNotificationChannels(): string[] {\r\n return Array.from(getRegistry().keys());\r\n}\r\n\r\n/**\r\n * Deliver a notification.\r\n *\r\n * The `inbox` channel always persists a row that drives the in-app UI\r\n * (bell + toast). Additional channels (webhook, custom) run in parallel,\r\n * best-effort. Returns the stored Notification when `inbox` ran, otherwise\r\n * `undefined`.\r\n *\r\n * Also emits `notification.sent` on the event bus so automations can react\r\n * to notifications (e.g. \"when a critical notification fires, also page me\").\r\n */\r\nconst MAX_TITLE_LEN = 100;\r\nconst MAX_BODY_LEN = 2000;\r\n\r\nexport async function notify(\r\n input: NotificationInput,\r\n meta: NotificationMeta,\r\n): Promise<Notification | undefined> {\r\n return (await notifyWithDelivery(input, meta)).notification;\r\n}\r\n\r\nexport async function notifyWithDelivery(\r\n input: NotificationInput,\r\n meta: NotificationMeta,\r\n): Promise<NotificationDeliveryResult> {\r\n if (!meta?.owner) {\r\n throw new Error(\"notify: meta.owner is required\");\r\n }\r\n input = {\r\n ...input,\r\n title: truncate(input.title, MAX_TITLE_LEN),\r\n body: truncate(input.body, MAX_BODY_LEN),\r\n };\r\n const channels = selectChannels(input.channels);\r\n const storedMetadata = scrubStoredMetadata(input.metadata);\r\n\r\n // The inbox channel is always included unless explicitly excluded.\r\n const runInbox = !input.channels || input.channels.includes(\"inbox\");\r\n const delivered: string[] = [];\r\n let stored: Notification | undefined;\r\n\r\n if (runInbox) {\r\n try {\r\n // Stored with just \"inbox\" first; the real delivered list is written\r\n // after fan-out so a failing webhook doesn't claim it was delivered.\r\n stored = await insertNotification({\r\n owner: meta.owner,\r\n severity: input.severity,\r\n title: input.title,\r\n body: input.body,\r\n metadata: storedMetadata,\r\n deliveredChannels: [\"inbox\"],\r\n });\r\n delivered.push(\"inbox\");\r\n } catch (err) {\r\n console.error(\"[notifications] inbox persist failed:\", err);\r\n }\r\n }\r\n\r\n // Await every channel so a 500-ing webhook doesn't end up in `delivered`.\r\n const results = await Promise.allSettled(\r\n channels.map(async (channel) => {\r\n const delivered = await channel.deliver(input, meta);\r\n // Explicit `false` means the channel skipped (no URL / recipients).\r\n if (delivered === false) return null;\r\n return channel.name;\r\n }),\r\n );\r\n results.forEach((r, i) => {\r\n if (r.status === \"fulfilled\") {\r\n if (r.value) delivered.push(r.value);\r\n } else {\r\n console.error(\r\n `[notifications] channel \"${channels[i].name}\" failed:`,\r\n r.reason,\r\n );\r\n }\r\n });\r\n\r\n const hasExtraChannel = delivered.some((c) => c !== \"inbox\");\r\n if (stored && hasExtraChannel) {\r\n try {\r\n await updateDeliveredChannels(stored.id, delivered);\r\n stored = { ...stored, deliveredChannels: delivered };\r\n } catch (err) {\r\n console.error(\"[notifications] delivered-channel update failed:\", err);\r\n }\r\n }\r\n\r\n // Only emit when at least one channel delivered — an emission with an\r\n // empty delivery list (and likely a null notificationId) would mislead\r\n // any automation chaining off this event.\r\n if (delivered.length > 0) {\r\n try {\r\n emitBusEvent(\r\n \"notification.sent\",\r\n {\r\n notificationId: stored?.id,\r\n severity: input.severity,\r\n title: input.title,\r\n body: input.body,\r\n deliveredChannels: delivered,\r\n },\r\n { owner: meta.owner },\r\n );\r\n } catch {\r\n // best-effort\r\n }\r\n }\r\n\r\n return { notification: stored, deliveredChannels: delivered };\r\n}\r\n\r\nfunction scrubStoredMetadata(\r\n metadata: Record<string, unknown> | undefined,\r\n): Record<string, unknown> | undefined {\r\n if (!metadata) return undefined;\r\n const entries = Object.entries(metadata).filter(\r\n ([key]) =>\r\n key !== \"delivery\" && key !== \"webhookUrl\" && key !== \"slackWebhookUrl\",\r\n );\r\n return entries.length ? Object.fromEntries(entries) : undefined;\r\n}\r\n\r\nfunction selectChannels(allowlist?: string[]): NotificationChannel[] {\r\n const registry = getRegistry();\r\n const all = Array.from(registry.values());\r\n if (!allowlist) return all;\r\n return all.filter((c) => allowlist.includes(c.name));\r\n}\r\n\r\n/** Test helper — drops all registered channels. */\r\nexport function __resetNotificationChannels(): void {\r\n getRegistry().clear();\r\n}\r\n\r\nexport {\r\n listNotifications,\r\n markNotificationRead,\r\n markAllNotificationsRead,\r\n deleteNotification,\r\n countUnread,\r\n} from \"./store.js\";\r\n"]}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/notifications/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\n\nimport { emit as emitBusEvent } from \"../event-bus/bus.js\";\nimport { registerEvent } from \"../event-bus/registry.js\";\nimport type { EventDefinition } from \"../event-bus/types.js\";\nimport { getScopedGlobal } from \"../shared/global-scope.js\";\nimport { truncate } from \"../shared/truncate.js\";\nimport { insertNotification, updateDeliveredChannels } from \"./store.js\";\nimport {\n NOTIFICATION_SEVERITIES,\n type NotificationChannel,\n type NotificationInput,\n type NotificationMeta,\n type Notification,\n} from \"./types.js\";\n\nexport interface NotificationDeliveryResult {\n notification?: Notification;\n deliveredChannels: string[];\n}\n\nregisterEvent({\n name: \"notification.sent\",\n description:\n \"Fires after notify() delivers to at least one channel. Automations can chain off this — e.g. fan critical notifications to Slack.\",\n payloadSchema: z.object({\n notificationId: z.string().optional(),\n severity: z.enum(NOTIFICATION_SEVERITIES),\n title: z.string(),\n body: z.string().optional(),\n deliveredChannels: z.array(z.string()),\n }) as unknown as EventDefinition[\"payloadSchema\"],\n example: {\n notificationId: \"ntf_abc\",\n severity: \"critical\",\n title: \"Payment failed\",\n body: \"Card ending 4242 declined\",\n deliveredChannels: [\"inbox\", \"webhook\"],\n },\n});\n\nconst REGISTRY_BASE_KEY = \"agent-native.notifications.registry\";\n\n// globalThis-pinned so one app's ESM graphs share one channel registry, but\n// scope-aware + lazily resolved so unified workspace deployments (all apps in\n// one isolate) keep per-app channels. See shared/global-scope.\nfunction getRegistry(): Map<string, NotificationChannel> {\n return getScopedGlobal(\n REGISTRY_BASE_KEY,\n () => new Map<string, NotificationChannel>(),\n );\n}\n\nexport function registerNotificationChannel(\n channel: NotificationChannel,\n): void {\n if (!channel?.name) {\n throw new Error(\"registerNotificationChannel: channel.name is required\");\n }\n if (typeof channel.deliver !== \"function\") {\n throw new Error(\n \"registerNotificationChannel: channel.deliver must be a function\",\n );\n }\n getRegistry().set(channel.name, channel);\n}\n\nexport function unregisterNotificationChannel(name: string): boolean {\n return getRegistry().delete(name);\n}\n\nexport function listNotificationChannels(): string[] {\n return Array.from(getRegistry().keys());\n}\n\n/**\n * Deliver a notification.\n *\n * The `inbox` channel always persists a row that drives the in-app UI\n * (bell + toast). Additional channels (webhook, custom) run in parallel,\n * best-effort. Returns the stored Notification when `inbox` ran, otherwise\n * `undefined`.\n *\n * Also emits `notification.sent` on the event bus so automations can react\n * to notifications (e.g. \"when a critical notification fires, also page me\").\n */\nconst MAX_TITLE_LEN = 100;\nconst MAX_BODY_LEN = 2000;\n\nexport async function notify(\n input: NotificationInput,\n meta: NotificationMeta,\n): Promise<Notification | undefined> {\n return (await notifyWithDelivery(input, meta)).notification;\n}\n\nexport async function notifyWithDelivery(\n input: NotificationInput,\n meta: NotificationMeta,\n): Promise<NotificationDeliveryResult> {\n if (!meta?.owner) {\n throw new Error(\"notify: meta.owner is required\");\n }\n input = {\n ...input,\n title: truncate(input.title, MAX_TITLE_LEN),\n body: truncate(input.body, MAX_BODY_LEN),\n };\n const channels = selectChannels(input.channels);\n const storedMetadata = scrubStoredMetadata(input.metadata);\n\n // The inbox channel is always included unless explicitly excluded.\n const runInbox = !input.channels || input.channels.includes(\"inbox\");\n const delivered: string[] = [];\n let stored: Notification | undefined;\n\n if (runInbox) {\n try {\n // Stored with just \"inbox\" first; the real delivered list is written\n // after fan-out so a failing webhook doesn't claim it was delivered.\n stored = await insertNotification({\n owner: meta.owner,\n severity: input.severity,\n title: input.title,\n body: input.body,\n metadata: storedMetadata,\n deliveredChannels: [\"inbox\"],\n });\n delivered.push(\"inbox\");\n } catch (err) {\n console.error(\"[notifications] inbox persist failed:\", err);\n }\n }\n\n // Await every channel so a 500-ing webhook doesn't end up in `delivered`.\n const results = await Promise.allSettled(\n channels.map(async (channel) => {\n const delivered = await channel.deliver(input, meta);\n // Explicit `false` means the channel skipped (no URL / recipients).\n if (delivered === false) return null;\n return channel.name;\n }),\n );\n results.forEach((r, i) => {\n if (r.status === \"fulfilled\") {\n if (r.value) delivered.push(r.value);\n } else {\n console.error(\n `[notifications] channel \"${channels[i].name}\" failed:`,\n r.reason,\n );\n }\n });\n\n const hasExtraChannel = delivered.some((c) => c !== \"inbox\");\n if (stored && hasExtraChannel) {\n try {\n await updateDeliveredChannels(stored.id, delivered);\n stored = { ...stored, deliveredChannels: delivered };\n } catch (err) {\n console.error(\"[notifications] delivered-channel update failed:\", err);\n }\n }\n\n // Only emit when at least one channel delivered — an emission with an\n // empty delivery list (and likely a null notificationId) would mislead\n // any automation chaining off this event.\n if (delivered.length > 0) {\n try {\n emitBusEvent(\n \"notification.sent\",\n {\n notificationId: stored?.id,\n severity: input.severity,\n title: input.title,\n body: input.body,\n deliveredChannels: delivered,\n },\n { owner: meta.owner },\n );\n } catch {\n // best-effort\n }\n }\n\n return { notification: stored, deliveredChannels: delivered };\n}\n\nfunction scrubStoredMetadata(\n metadata: Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n if (!metadata) return undefined;\n const entries = Object.entries(metadata).filter(\n ([key]) =>\n key !== \"delivery\" && key !== \"webhookUrl\" && key !== \"slackWebhookUrl\",\n );\n return entries.length ? Object.fromEntries(entries) : undefined;\n}\n\nfunction selectChannels(allowlist?: string[]): NotificationChannel[] {\n const registry = getRegistry();\n const all = Array.from(registry.values());\n if (!allowlist) return all;\n return all.filter((c) => allowlist.includes(c.name));\n}\n\n/** Test helper — drops all registered channels. */\nexport function __resetNotificationChannels(): void {\n getRegistry().clear();\n}\n\nexport {\n listNotifications,\n markNotificationRead,\n markAllNotificationsRead,\n deleteNotification,\n countUnread,\n} from \"./store.js\";\n"]}
@@ -41,28 +41,28 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
41
41
  thumbsUpRate: number;
42
42
  avgEvalScore: number;
43
43
  } | {
44
+ error?: undefined;
45
+ ok?: undefined;
44
46
  summary: import("./types.js").TraceSummary;
45
47
  spans: import("./types.js").TraceSpan[];
46
48
  id?: undefined;
49
+ } | {
47
50
  error?: undefined;
48
51
  ok?: undefined;
49
- } | {
50
52
  summary?: undefined;
51
53
  spans?: undefined;
52
54
  id: string;
53
- error?: undefined;
54
- ok?: undefined;
55
55
  } | {
56
+ ok?: undefined;
56
57
  summary?: undefined;
57
58
  spans?: undefined;
58
59
  id?: undefined;
59
60
  error: any;
60
- ok?: undefined;
61
61
  } | {
62
+ error?: undefined;
62
63
  summary?: undefined;
63
64
  spans?: undefined;
64
65
  id?: undefined;
65
- error?: undefined;
66
66
  ok: boolean;
67
67
  }>>;
68
68
  //# sourceMappingURL=routes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/private-blob/registry.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAmKpB,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,mBAAmB,GAC5B,IAAI,CAEN;AAED,wBAAgB,6BAA6B,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE9D;AAED,wBAAgB,wBAAwB,IAAI,mBAAmB,EAAE,CAEhE;AAED,wBAAgB,4BAA4B,IAAI,mBAAmB,GAAG,IAAI,CAKzE;AAED,wBAAgB,yCAAyC,CACvD,OAAO,EAAE,OAAO,GACf,IAAI,CAEN;AAED,wBAAsB,cAAc,CAClC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAQnC;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,qBAAqB,CAAC,CAOhC;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,uBAAuB,CAAC,CAYlC"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/private-blob/registry.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAsKpB,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,mBAAmB,GAC5B,IAAI,CAEN;AAED,wBAAgB,6BAA6B,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE9D;AAED,wBAAgB,wBAAwB,IAAI,mBAAmB,EAAE,CAEhE;AAED,wBAAgB,4BAA4B,IAAI,mBAAmB,GAAG,IAAI,CAKzE;AAED,wBAAgB,yCAAyC,CACvD,OAAO,EAAE,OAAO,GACf,IAAI,CAEN;AAED,wBAAsB,cAAc,CAClC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAQnC;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,qBAAqB,CAAC,CAOhC;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,uBAAuB,CAAC,CAYlC"}
@@ -1,12 +1,17 @@
1
1
  import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
2
2
  import { uploadFile } from "../file-upload/index.js";
3
3
  import { decryptSecretValue, encryptSecretValue, getSecretEncryptionKey, } from "../secrets/crypto.js";
4
+ import { getScopedGlobal } from "../shared/global-scope.js";
4
5
  const PUBLIC_UPLOAD_HANDLE_PREFIX = "public-upload:v1:";
5
- const globals = globalThis;
6
- const providers = (globals.__agentNativePrivateBlobProviders ??= new Map());
7
- const publicUploadFallbackRef = (globals.__agentNativePrivateBlobPublicUploadFallback ??= {
8
- enabled: true,
9
- });
6
+ // globalThis-pinned so an app's multiple ESM graphs share one registry, but
7
+ // scope-aware + lazily resolved so unified workspace deployments (all apps
8
+ // in one isolate) keep per-app provider registries. See shared/global-scope.
9
+ function getProviders() {
10
+ return getScopedGlobal("agent-native.private-blob.providers", () => new Map());
11
+ }
12
+ function getPublicUploadFallbackRef() {
13
+ return getScopedGlobal("agent-native.private-blob.public-upload-fallback", () => ({ enabled: true }));
14
+ }
10
15
  function toBytes(data) {
11
16
  return data instanceof Uint8Array ? data : new Uint8Array(data);
12
17
  }
@@ -99,29 +104,29 @@ async function readViaEncryptedPublicUpload(handle) {
99
104
  };
100
105
  }
101
106
  export function registerPrivateBlobProvider(provider) {
102
- providers.set(provider.id, provider);
107
+ getProviders().set(provider.id, provider);
103
108
  }
104
109
  export function unregisterPrivateBlobProvider(id) {
105
- providers.delete(id);
110
+ getProviders().delete(id);
106
111
  }
107
112
  export function listPrivateBlobProviders() {
108
- return [...providers.values()];
113
+ return [...getProviders().values()];
109
114
  }
110
115
  export function getActivePrivateBlobProvider() {
111
- for (const provider of providers.values()) {
116
+ for (const provider of getProviders().values()) {
112
117
  if (provider.isConfigured())
113
118
  return provider;
114
119
  }
115
120
  return null;
116
121
  }
117
122
  export function setPrivateBlobPublicUploadFallbackEnabled(enabled) {
118
- publicUploadFallbackRef.enabled = enabled;
123
+ getPublicUploadFallbackRef().enabled = enabled;
119
124
  }
120
125
  export async function putPrivateBlob(input) {
121
126
  const provider = getActivePrivateBlobProvider();
122
127
  if (provider)
123
128
  return provider.put(input);
124
- if (!publicUploadFallbackRef.enabled)
129
+ if (!getPublicUploadFallbackRef().enabled)
125
130
  return null;
126
131
  if (process.env.AGENT_NATIVE_PRIVATE_BLOB_PUBLIC_UPLOAD_FALLBACK === "0") {
127
132
  return null;
@@ -129,7 +134,7 @@ export async function putPrivateBlob(input) {
129
134
  return putViaEncryptedPublicUpload(input);
130
135
  }
131
136
  export async function readPrivateBlob(handle) {
132
- const provider = providers.get(handle.provider);
137
+ const provider = getProviders().get(handle.provider);
133
138
  if (provider)
134
139
  return provider.read(handle);
135
140
  if (isPublicUploadFallbackHandle(handle)) {
@@ -138,7 +143,7 @@ export async function readPrivateBlob(handle) {
138
143
  throw new Error(`No private blob provider registered for ${handle.provider}`);
139
144
  }
140
145
  export async function deletePrivateBlob(handle) {
141
- const provider = providers.get(handle.provider);
146
+ const provider = getProviders().get(handle.provider);
142
147
  if (provider)
143
148
  return provider.delete(handle);
144
149
  if (isPublicUploadFallbackHandle(handle)) {
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/private-blob/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { createCipheriv, createDecipheriv, randomBytes } from \"node:crypto\";\r\n\r\nimport { uploadFile } from \"../file-upload/index.js\";\r\nimport {\r\n decryptSecretValue,\r\n encryptSecretValue,\r\n getSecretEncryptionKey,\r\n} from \"../secrets/crypto.js\";\r\nimport type {\r\n PrivateBlobDeleteResult,\r\n PrivateBlobHandle,\r\n PrivateBlobProvider,\r\n PrivateBlobPutInput,\r\n PrivateBlobReadResult,\r\n} from \"./types.js\";\r\n\r\ninterface PrivateBlobGlobals {\r\n __agentNativePrivateBlobProviders?: Map<string, PrivateBlobProvider>;\r\n __agentNativePrivateBlobPublicUploadFallback?: { enabled: boolean };\r\n}\r\n\r\ninterface EncryptedPayload {\r\n iv: string;\r\n tag: string;\r\n ciphertext: Uint8Array;\r\n}\r\n\r\ninterface EncryptionParams {\r\n iv: string;\r\n tag: string;\r\n}\r\n\r\ninterface PublicUploadDescriptor {\r\n kind: \"agent-native.private-blob.public-upload\";\r\n version: 1;\r\n url: string;\r\n uploadProvider: string;\r\n uploadId?: string;\r\n encryption: EncryptionParams;\r\n mimeType?: string;\r\n metadata?: PrivateBlobHandle[\"metadata\"];\r\n size: number;\r\n createdAt: string;\r\n}\r\n\r\nconst PUBLIC_UPLOAD_HANDLE_PREFIX = \"public-upload:v1:\";\r\nconst globals = globalThis as typeof globalThis & PrivateBlobGlobals;\r\nconst providers: Map<string, PrivateBlobProvider> =\r\n (globals.__agentNativePrivateBlobProviders ??= new Map());\r\nconst publicUploadFallbackRef: { enabled: boolean } =\r\n (globals.__agentNativePrivateBlobPublicUploadFallback ??= {\r\n enabled: true,\r\n });\r\n\r\nfunction toBytes(data: Uint8Array | Buffer): Uint8Array {\r\n return data instanceof Uint8Array ? data : new Uint8Array(data);\r\n}\r\n\r\nfunction encryptBytes(data: Uint8Array): EncryptedPayload {\r\n const iv = randomBytes(12);\r\n const cipher = createCipheriv(\"aes-256-gcm\", getSecretEncryptionKey(), iv);\r\n const ciphertext = Buffer.concat([\r\n cipher.update(Buffer.from(data)),\r\n cipher.final(),\r\n ]);\r\n return {\r\n iv: iv.toString(\"base64url\"),\r\n ciphertext: new Uint8Array(ciphertext),\r\n tag: cipher.getAuthTag().toString(\"base64url\"),\r\n };\r\n}\r\n\r\nfunction decryptBytes(\r\n params: EncryptionParams,\r\n ciphertext: Uint8Array,\r\n): Uint8Array {\r\n const decipher = createDecipheriv(\r\n \"aes-256-gcm\",\r\n getSecretEncryptionKey(),\r\n Buffer.from(params.iv, \"base64url\"),\r\n );\r\n decipher.setAuthTag(Buffer.from(params.tag, \"base64url\"));\r\n return new Uint8Array(\r\n Buffer.concat([decipher.update(Buffer.from(ciphertext)), decipher.final()]),\r\n );\r\n}\r\n\r\nfunction encodePublicUploadDescriptor(\r\n descriptor: PublicUploadDescriptor,\r\n): string {\r\n return `${PUBLIC_UPLOAD_HANDLE_PREFIX}${encryptSecretValue(\r\n JSON.stringify(descriptor),\r\n )}`;\r\n}\r\n\r\nfunction decodePublicUploadDescriptor(id: string): PublicUploadDescriptor {\r\n if (!id.startsWith(PUBLIC_UPLOAD_HANDLE_PREFIX)) {\r\n throw new Error(\r\n \"Private blob handle is not a public-upload fallback handle\",\r\n );\r\n }\r\n const raw = decryptSecretValue(id.slice(PUBLIC_UPLOAD_HANDLE_PREFIX.length));\r\n const descriptor = JSON.parse(raw) as PublicUploadDescriptor;\r\n if (\r\n descriptor?.kind !== \"agent-native.private-blob.public-upload\" ||\r\n descriptor.version !== 1 ||\r\n typeof descriptor.url !== \"string\"\r\n ) {\r\n throw new Error(\"Private blob handle descriptor is invalid\");\r\n }\r\n return descriptor;\r\n}\r\n\r\nfunction isPublicUploadFallbackHandle(handle: PrivateBlobHandle): boolean {\r\n return handle.id.startsWith(PUBLIC_UPLOAD_HANDLE_PREFIX);\r\n}\r\n\r\nasync function putViaEncryptedPublicUpload(\r\n input: PrivateBlobPutInput,\r\n): Promise<PrivateBlobHandle | null> {\r\n const bytes = toBytes(input.data);\r\n const encrypted = encryptBytes(bytes);\r\n const uploaded = await uploadFile({\r\n data: Buffer.from(encrypted.ciphertext),\r\n filename: input.filename ?? input.key ?? \"private-blob.bin\",\r\n mimeType: \"application/octet-stream\",\r\n ownerEmail: input.ownerEmail,\r\n recordAsset: false,\r\n });\r\n if (!uploaded) return null;\r\n\r\n const descriptor: PublicUploadDescriptor = {\r\n kind: \"agent-native.private-blob.public-upload\",\r\n version: 1,\r\n url: uploaded.url,\r\n uploadProvider: uploaded.provider,\r\n uploadId: uploaded.id,\r\n encryption: { iv: encrypted.iv, tag: encrypted.tag },\r\n mimeType: input.mimeType,\r\n metadata: input.metadata,\r\n size: bytes.byteLength,\r\n createdAt: new Date().toISOString(),\r\n };\r\n\r\n return {\r\n id: encodePublicUploadDescriptor(descriptor),\r\n provider: `public-upload:${uploaded.provider}`,\r\n opaque: true,\r\n encrypted: true,\r\n mimeType: input.mimeType,\r\n size: bytes.byteLength,\r\n createdAt: descriptor.createdAt,\r\n metadata: input.metadata,\r\n };\r\n}\r\n\r\nasync function readViaEncryptedPublicUpload(\r\n handle: PrivateBlobHandle,\r\n): Promise<PrivateBlobReadResult> {\r\n const descriptor = decodePublicUploadDescriptor(handle.id);\r\n const response = await fetch(descriptor.url);\r\n if (!response.ok) {\r\n throw new Error(\r\n `Private blob public-upload read failed (${response.status}): ${response.statusText}`,\r\n );\r\n }\r\n // The uploaded ciphertext is intentionally opaque; the descriptor carries\r\n // auth tag + IV separately so the backing public URL is useless by itself.\r\n const ciphertext = new Uint8Array(await response.arrayBuffer());\r\n return {\r\n data: decryptBytes(descriptor.encryption, ciphertext),\r\n mimeType: descriptor.mimeType,\r\n metadata: descriptor.metadata,\r\n handle,\r\n };\r\n}\r\n\r\nexport function registerPrivateBlobProvider(\r\n provider: PrivateBlobProvider,\r\n): void {\r\n providers.set(provider.id, provider);\r\n}\r\n\r\nexport function unregisterPrivateBlobProvider(id: string): void {\r\n providers.delete(id);\r\n}\r\n\r\nexport function listPrivateBlobProviders(): PrivateBlobProvider[] {\r\n return [...providers.values()];\r\n}\r\n\r\nexport function getActivePrivateBlobProvider(): PrivateBlobProvider | null {\r\n for (const provider of providers.values()) {\r\n if (provider.isConfigured()) return provider;\r\n }\r\n return null;\r\n}\r\n\r\nexport function setPrivateBlobPublicUploadFallbackEnabled(\r\n enabled: boolean,\r\n): void {\r\n publicUploadFallbackRef.enabled = enabled;\r\n}\r\n\r\nexport async function putPrivateBlob(\r\n input: PrivateBlobPutInput,\r\n): Promise<PrivateBlobHandle | null> {\r\n const provider = getActivePrivateBlobProvider();\r\n if (provider) return provider.put(input);\r\n if (!publicUploadFallbackRef.enabled) return null;\r\n if (process.env.AGENT_NATIVE_PRIVATE_BLOB_PUBLIC_UPLOAD_FALLBACK === \"0\") {\r\n return null;\r\n }\r\n return putViaEncryptedPublicUpload(input);\r\n}\r\n\r\nexport async function readPrivateBlob(\r\n handle: PrivateBlobHandle,\r\n): Promise<PrivateBlobReadResult> {\r\n const provider = providers.get(handle.provider);\r\n if (provider) return provider.read(handle);\r\n if (isPublicUploadFallbackHandle(handle)) {\r\n return readViaEncryptedPublicUpload(handle);\r\n }\r\n throw new Error(`No private blob provider registered for ${handle.provider}`);\r\n}\r\n\r\nexport async function deletePrivateBlob(\r\n handle: PrivateBlobHandle,\r\n): Promise<PrivateBlobDeleteResult> {\r\n const provider = providers.get(handle.provider);\r\n if (provider) return provider.delete(handle);\r\n if (isPublicUploadFallbackHandle(handle)) {\r\n return {\r\n deleted: false,\r\n provider: handle.provider,\r\n reason:\r\n \"delete is not supported by the encrypted public-upload fallback provider\",\r\n };\r\n }\r\n throw new Error(`No private blob provider registered for ${handle.provider}`);\r\n}\r\n"]}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/private-blob/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { createCipheriv, createDecipheriv, randomBytes } from \"node:crypto\";\n\nimport { uploadFile } from \"../file-upload/index.js\";\nimport {\n decryptSecretValue,\n encryptSecretValue,\n getSecretEncryptionKey,\n} from \"../secrets/crypto.js\";\nimport { getScopedGlobal } from \"../shared/global-scope.js\";\nimport type {\n PrivateBlobDeleteResult,\n PrivateBlobHandle,\n PrivateBlobProvider,\n PrivateBlobPutInput,\n PrivateBlobReadResult,\n} from \"./types.js\";\n\ninterface EncryptedPayload {\n iv: string;\n tag: string;\n ciphertext: Uint8Array;\n}\n\ninterface EncryptionParams {\n iv: string;\n tag: string;\n}\n\ninterface PublicUploadDescriptor {\n kind: \"agent-native.private-blob.public-upload\";\n version: 1;\n url: string;\n uploadProvider: string;\n uploadId?: string;\n encryption: EncryptionParams;\n mimeType?: string;\n metadata?: PrivateBlobHandle[\"metadata\"];\n size: number;\n createdAt: string;\n}\n\nconst PUBLIC_UPLOAD_HANDLE_PREFIX = \"public-upload:v1:\";\n// globalThis-pinned so an app's multiple ESM graphs share one registry, but\n// scope-aware + lazily resolved so unified workspace deployments (all apps\n// in one isolate) keep per-app provider registries. See shared/global-scope.\nfunction getProviders(): Map<string, PrivateBlobProvider> {\n return getScopedGlobal(\n \"agent-native.private-blob.providers\",\n () => new Map<string, PrivateBlobProvider>(),\n );\n}\nfunction getPublicUploadFallbackRef(): { enabled: boolean } {\n return getScopedGlobal(\n \"agent-native.private-blob.public-upload-fallback\",\n () => ({ enabled: true }),\n );\n}\n\nfunction toBytes(data: Uint8Array | Buffer): Uint8Array {\n return data instanceof Uint8Array ? data : new Uint8Array(data);\n}\n\nfunction encryptBytes(data: Uint8Array): EncryptedPayload {\n const iv = randomBytes(12);\n const cipher = createCipheriv(\"aes-256-gcm\", getSecretEncryptionKey(), iv);\n const ciphertext = Buffer.concat([\n cipher.update(Buffer.from(data)),\n cipher.final(),\n ]);\n return {\n iv: iv.toString(\"base64url\"),\n ciphertext: new Uint8Array(ciphertext),\n tag: cipher.getAuthTag().toString(\"base64url\"),\n };\n}\n\nfunction decryptBytes(\n params: EncryptionParams,\n ciphertext: Uint8Array,\n): Uint8Array {\n const decipher = createDecipheriv(\n \"aes-256-gcm\",\n getSecretEncryptionKey(),\n Buffer.from(params.iv, \"base64url\"),\n );\n decipher.setAuthTag(Buffer.from(params.tag, \"base64url\"));\n return new Uint8Array(\n Buffer.concat([decipher.update(Buffer.from(ciphertext)), decipher.final()]),\n );\n}\n\nfunction encodePublicUploadDescriptor(\n descriptor: PublicUploadDescriptor,\n): string {\n return `${PUBLIC_UPLOAD_HANDLE_PREFIX}${encryptSecretValue(\n JSON.stringify(descriptor),\n )}`;\n}\n\nfunction decodePublicUploadDescriptor(id: string): PublicUploadDescriptor {\n if (!id.startsWith(PUBLIC_UPLOAD_HANDLE_PREFIX)) {\n throw new Error(\n \"Private blob handle is not a public-upload fallback handle\",\n );\n }\n const raw = decryptSecretValue(id.slice(PUBLIC_UPLOAD_HANDLE_PREFIX.length));\n const descriptor = JSON.parse(raw) as PublicUploadDescriptor;\n if (\n descriptor?.kind !== \"agent-native.private-blob.public-upload\" ||\n descriptor.version !== 1 ||\n typeof descriptor.url !== \"string\"\n ) {\n throw new Error(\"Private blob handle descriptor is invalid\");\n }\n return descriptor;\n}\n\nfunction isPublicUploadFallbackHandle(handle: PrivateBlobHandle): boolean {\n return handle.id.startsWith(PUBLIC_UPLOAD_HANDLE_PREFIX);\n}\n\nasync function putViaEncryptedPublicUpload(\n input: PrivateBlobPutInput,\n): Promise<PrivateBlobHandle | null> {\n const bytes = toBytes(input.data);\n const encrypted = encryptBytes(bytes);\n const uploaded = await uploadFile({\n data: Buffer.from(encrypted.ciphertext),\n filename: input.filename ?? input.key ?? \"private-blob.bin\",\n mimeType: \"application/octet-stream\",\n ownerEmail: input.ownerEmail,\n recordAsset: false,\n });\n if (!uploaded) return null;\n\n const descriptor: PublicUploadDescriptor = {\n kind: \"agent-native.private-blob.public-upload\",\n version: 1,\n url: uploaded.url,\n uploadProvider: uploaded.provider,\n uploadId: uploaded.id,\n encryption: { iv: encrypted.iv, tag: encrypted.tag },\n mimeType: input.mimeType,\n metadata: input.metadata,\n size: bytes.byteLength,\n createdAt: new Date().toISOString(),\n };\n\n return {\n id: encodePublicUploadDescriptor(descriptor),\n provider: `public-upload:${uploaded.provider}`,\n opaque: true,\n encrypted: true,\n mimeType: input.mimeType,\n size: bytes.byteLength,\n createdAt: descriptor.createdAt,\n metadata: input.metadata,\n };\n}\n\nasync function readViaEncryptedPublicUpload(\n handle: PrivateBlobHandle,\n): Promise<PrivateBlobReadResult> {\n const descriptor = decodePublicUploadDescriptor(handle.id);\n const response = await fetch(descriptor.url);\n if (!response.ok) {\n throw new Error(\n `Private blob public-upload read failed (${response.status}): ${response.statusText}`,\n );\n }\n // The uploaded ciphertext is intentionally opaque; the descriptor carries\n // auth tag + IV separately so the backing public URL is useless by itself.\n const ciphertext = new Uint8Array(await response.arrayBuffer());\n return {\n data: decryptBytes(descriptor.encryption, ciphertext),\n mimeType: descriptor.mimeType,\n metadata: descriptor.metadata,\n handle,\n };\n}\n\nexport function registerPrivateBlobProvider(\n provider: PrivateBlobProvider,\n): void {\n getProviders().set(provider.id, provider);\n}\n\nexport function unregisterPrivateBlobProvider(id: string): void {\n getProviders().delete(id);\n}\n\nexport function listPrivateBlobProviders(): PrivateBlobProvider[] {\n return [...getProviders().values()];\n}\n\nexport function getActivePrivateBlobProvider(): PrivateBlobProvider | null {\n for (const provider of getProviders().values()) {\n if (provider.isConfigured()) return provider;\n }\n return null;\n}\n\nexport function setPrivateBlobPublicUploadFallbackEnabled(\n enabled: boolean,\n): void {\n getPublicUploadFallbackRef().enabled = enabled;\n}\n\nexport async function putPrivateBlob(\n input: PrivateBlobPutInput,\n): Promise<PrivateBlobHandle | null> {\n const provider = getActivePrivateBlobProvider();\n if (provider) return provider.put(input);\n if (!getPublicUploadFallbackRef().enabled) return null;\n if (process.env.AGENT_NATIVE_PRIVATE_BLOB_PUBLIC_UPLOAD_FALLBACK === \"0\") {\n return null;\n }\n return putViaEncryptedPublicUpload(input);\n}\n\nexport async function readPrivateBlob(\n handle: PrivateBlobHandle,\n): Promise<PrivateBlobReadResult> {\n const provider = getProviders().get(handle.provider);\n if (provider) return provider.read(handle);\n if (isPublicUploadFallbackHandle(handle)) {\n return readViaEncryptedPublicUpload(handle);\n }\n throw new Error(`No private blob provider registered for ${handle.provider}`);\n}\n\nexport async function deletePrivateBlob(\n handle: PrivateBlobHandle,\n): Promise<PrivateBlobDeleteResult> {\n const provider = getProviders().get(handle.provider);\n if (provider) return provider.delete(handle);\n if (isPublicUploadFallbackHandle(handle)) {\n return {\n deleted: false,\n provider: handle.provider,\n reason:\n \"delete is not supported by the encrypted public-upload fallback provider\",\n };\n }\n throw new Error(`No private blob provider registered for ${handle.provider}`);\n}\n"]}
@@ -15,7 +15,7 @@ export declare function createProgressHandler(): import("h3").EventHandlerWithFe
15
15
  error: string;
16
16
  ok?: undefined;
17
17
  } | {
18
- ok: boolean;
19
18
  error?: undefined;
19
+ ok: boolean;
20
20
  }>>;
21
21
  //# sourceMappingURL=routes.d.ts.map
@@ -52,8 +52,8 @@ export declare function handleDeleteResource(event: any): Promise<{
52
52
  error: string;
53
53
  ok?: undefined;
54
54
  } | {
55
- ok: boolean;
56
55
  error?: undefined;
56
+ ok: boolean;
57
57
  }>;
58
58
  /** POST /_agent-native/resources/upload — upload a file as a resource */
59
59
  export declare function handleUploadResource(event: any): Promise<import("./store.js").Resource | {
@@ -74,10 +74,10 @@ export declare function handleUploadResource(event: any): Promise<import("./stor
74
74
  runId: string | null;
75
75
  expiresAt: number | null;
76
76
  metadata: string | null;
77
+ error?: undefined;
77
78
  url: string;
78
79
  provider: string;
79
80
  storageSetupRequired?: undefined;
80
- error?: undefined;
81
81
  } | {
82
82
  error: string;
83
83
  storageSetupRequired: boolean;