@jami-studio/core 0.92.27 → 0.92.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +17 -0
- package/corpus/core/package.json +2 -1
- package/corpus/core/src/deploy/build.ts +56 -2
- package/corpus/core/src/event-bus/bus.ts +138 -140
- package/corpus/core/src/event-bus/registry.ts +81 -79
- package/corpus/core/src/file-upload/registry.ts +135 -125
- package/corpus/core/src/notifications/registry.ts +218 -216
- package/corpus/core/src/private-blob/registry.ts +246 -242
- package/corpus/core/src/secrets/register.ts +132 -129
- package/corpus/core/src/shared/global-scope.ts +75 -0
- package/corpus/core/src/sharing/registry.ts +193 -194
- package/corpus/core/src/tracking/providers.ts +425 -422
- package/corpus/core/src/tracking/registry.ts +102 -102
- package/dist/collab/routes.d.ts +1 -1
- package/dist/deploy/build.d.ts +23 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +36 -2
- package/dist/deploy/build.js.map +1 -1
- package/dist/event-bus/bus.d.ts.map +1 -1
- package/dist/event-bus/bus.js +8 -6
- package/dist/event-bus/bus.js.map +1 -1
- package/dist/event-bus/registry.d.ts.map +1 -1
- package/dist/event-bus/registry.js +10 -6
- package/dist/event-bus/registry.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/file-upload/registry.d.ts.map +1 -1
- package/dist/file-upload/registry.js +28 -8
- package/dist/file-upload/registry.js.map +1 -1
- package/dist/notifications/registry.d.ts.map +1 -1
- package/dist/notifications/registry.js +6 -5
- package/dist/notifications/registry.js.map +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/private-blob/registry.d.ts.map +1 -1
- package/dist/private-blob/registry.js +18 -13
- package/dist/private-blob/registry.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/secrets/register.d.ts.map +1 -1
- package/dist/secrets/register.js +11 -7
- package/dist/secrets/register.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/shared/global-scope.d.ts +55 -0
- package/dist/shared/global-scope.d.ts.map +1 -0
- package/dist/shared/global-scope.js +70 -0
- package/dist/shared/global-scope.js.map +1 -0
- package/dist/sharing/registry.d.ts.map +1 -1
- package/dist/sharing/registry.js +2 -16
- package/dist/sharing/registry.js.map +1 -1
- package/dist/tracking/providers.d.ts.map +1 -1
- package/dist/tracking/providers.js +11 -9
- package/dist/tracking/providers.js.map +1 -1
- package/dist/tracking/registry.d.ts.map +1 -1
- package/dist/tracking/registry.js +5 -5
- package/dist/tracking/registry.js.map +1 -1
- package/package.json +2 -1
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.92.28
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 77b77e7: Per-app global-registry scoping on unified workspace deployments. The
|
|
8
|
+
globalThis-pinned framework registries (file-upload providers, private-blob
|
|
9
|
+
providers, shareable resources, event-bus registry + bus, notification
|
|
10
|
+
channels, tracking providers + queue, secrets registry) were shared across
|
|
11
|
+
every app in the single Cloudflare Pages isolate, so one app's registrations
|
|
12
|
+
served another app's requests — an upload POSTed to /assets was handled by
|
|
13
|
+
the clips app's S3 provider (wrong object prefix). Registries now resolve
|
|
14
|
+
their global keys lazily through a per-module-graph scope
|
|
15
|
+
(`@agent-native/core/global-scope`), and each mounted app's generated worker
|
|
16
|
+
entry sets its scope id via a `_scope-init.js` module evaluated first in the
|
|
17
|
+
bundle's import graph. Dev-mode and single-app deployments stay unscoped, so
|
|
18
|
+
the original multi-ESM-graph dedupe behavior is unchanged.
|
|
19
|
+
|
|
3
20
|
## 0.92.27
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.92.
|
|
3
|
+
"version": "0.92.28",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/studio-jami/jami-studio#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -97,6 +97,7 @@
|
|
|
97
97
|
"./embedding/react": "./dist/embedding/react.js",
|
|
98
98
|
"./onboarding": "./dist/onboarding/index.js",
|
|
99
99
|
"./shared": "./dist/shared/index.js",
|
|
100
|
+
"./global-scope": "./dist/shared/global-scope.js",
|
|
100
101
|
"./voice": "./dist/voice/index.js",
|
|
101
102
|
"./scripts": "./dist/scripts/index.js",
|
|
102
103
|
"./application-state": "./dist/application-state/index.js",
|
|
@@ -605,6 +605,46 @@ export const CLOUDFLARE_WORKER_NODE_BUILTIN_STUB_MODULES: Record<
|
|
|
605
605
|
|
|
606
606
|
export interface GenerateWorkerEntryOptions {
|
|
607
607
|
includeReactRouterSsr?: boolean;
|
|
608
|
+
/**
|
|
609
|
+
* Per-app global-registry scope id for unified workspace deployments.
|
|
610
|
+
* When set, the generated entry's FIRST import is `./_scope-init.js`
|
|
611
|
+
* (written by the build via `generateScopeInitSource`), which calls
|
|
612
|
+
* core's `setGlobalScopeId(appId)` before any other module in the app
|
|
613
|
+
* bundle evaluates — so every globalThis-pinned registry in this app's
|
|
614
|
+
* module graph resolves per-app keys instead of colliding with sibling
|
|
615
|
+
* apps in the shared isolate (issue: /assets uploads served by clips'
|
|
616
|
+
* provider).
|
|
617
|
+
*/
|
|
618
|
+
appScopeId?: string | null;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Source of the `_scope-init.js` module the worker entry imports first on
|
|
623
|
+
* unified workspace deployments. Must stay dependency-free beyond core's
|
|
624
|
+
* lean `global-scope` subpath so nothing registry-touching evaluates before
|
|
625
|
+
* the scope id is set (ESM evaluates imports depth-first in order).
|
|
626
|
+
*/
|
|
627
|
+
export function generateScopeInitSource(appScopeId: string): string {
|
|
628
|
+
return `// AUTO-GENERATED by @agent-native/core deploy build
|
|
629
|
+
// Scope globalThis-pinned framework registries to this app's module graph.
|
|
630
|
+
// Evaluated FIRST in the worker entry's import graph so the scope is set
|
|
631
|
+
// before any registry module initializes. See core shared/global-scope.
|
|
632
|
+
import { setGlobalScopeId } from "@agent-native/core/global-scope";
|
|
633
|
+
setGlobalScopeId(${JSON.stringify(appScopeId)});
|
|
634
|
+
`;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Derive the workspace app id from a mounted app base path ("/assets" →
|
|
639
|
+
* "assets"). Returns null for unmounted (root) builds.
|
|
640
|
+
*/
|
|
641
|
+
export function workspaceAppScopeIdFromBasePath(
|
|
642
|
+
basePath: string | undefined,
|
|
643
|
+
): string | null {
|
|
644
|
+
const normalized = normalizeAppBasePath(basePath ?? "");
|
|
645
|
+
if (!normalized) return null;
|
|
646
|
+
const [first] = normalized.slice(1).split("/");
|
|
647
|
+
return first || null;
|
|
608
648
|
}
|
|
609
649
|
|
|
610
650
|
interface ReactRouterAssetManifest {
|
|
@@ -734,6 +774,7 @@ export function generateWorkerEntry(
|
|
|
734
774
|
options: GenerateWorkerEntryOptions = {},
|
|
735
775
|
): string {
|
|
736
776
|
const includeReactRouterSsr = options.includeReactRouterSsr ?? true;
|
|
777
|
+
const appScopeId = options.appScopeId ?? null;
|
|
737
778
|
const routeImports: string[] = [];
|
|
738
779
|
const routeRegistrations: string[] = [];
|
|
739
780
|
|
|
@@ -854,7 +895,7 @@ export function generateWorkerEntry(
|
|
|
854
895
|
|
|
855
896
|
return `
|
|
856
897
|
// Auto-generated worker entry point for ${preset}
|
|
857
|
-
import { H3, defineEventHandler, readBody, toResponse } from "h3";
|
|
898
|
+
${appScopeId ? 'import "./_scope-init.js";\n' : ""}import { H3, defineEventHandler, readBody, toResponse } from "h3";
|
|
858
899
|
${includeReactRouterSsr ? 'import { createRequestHandler } from "react-router";' : ""}
|
|
859
900
|
${includeReactRouterSsr ? 'import * as serverBuild from "./server-build.js";' : ""}
|
|
860
901
|
|
|
@@ -1668,6 +1709,12 @@ async function buildCloudflarePages() {
|
|
|
1668
1709
|
|
|
1669
1710
|
// Generate the worker entry
|
|
1670
1711
|
const immutableAssetPaths = collectImmutableAssetPaths(clientDir);
|
|
1712
|
+
// Per-app global-registry scoping for unified workspace deploys: mounted
|
|
1713
|
+
// builds (APP_BASE_PATH=/<app>) get a scope-init module evaluated first so
|
|
1714
|
+
// globalThis-pinned registries stay per-app in the shared isolate.
|
|
1715
|
+
const appScopeId = workspaceAppScopeIdFromBasePath(
|
|
1716
|
+
normalizeConfiguredAppBasePath(),
|
|
1717
|
+
);
|
|
1671
1718
|
const entrySource = generateWorkerEntry(
|
|
1672
1719
|
routes,
|
|
1673
1720
|
plugins,
|
|
@@ -1676,7 +1723,7 @@ async function buildCloudflarePages() {
|
|
|
1676
1723
|
workspaceCore,
|
|
1677
1724
|
immutableAssetPaths,
|
|
1678
1725
|
normalizeConfiguredAppBasePath(),
|
|
1679
|
-
{ includeReactRouterSsr },
|
|
1726
|
+
{ includeReactRouterSsr, appScopeId },
|
|
1680
1727
|
);
|
|
1681
1728
|
|
|
1682
1729
|
// Create _worker.js output directory
|
|
@@ -1704,6 +1751,13 @@ async function buildCloudflarePages() {
|
|
|
1704
1751
|
const tmpEntry = path.join(tmpDir, "index.js");
|
|
1705
1752
|
fs.writeFileSync(tmpEntry, adjustedEntry);
|
|
1706
1753
|
|
|
1754
|
+
if (appScopeId) {
|
|
1755
|
+
fs.writeFileSync(
|
|
1756
|
+
path.join(tmpDir, "_scope-init.js"),
|
|
1757
|
+
generateScopeInitSource(appScopeId),
|
|
1758
|
+
);
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1707
1761
|
if (includeReactRouterSsr) {
|
|
1708
1762
|
copyDir(serverDir, path.join(tmpDir, "server"));
|
|
1709
1763
|
}
|
|
@@ -1,140 +1,138 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Typed pub/sub bus for framework events.
|
|
3
|
-
*
|
|
4
|
-
* Wraps Node's EventEmitter with payload validation against the registered
|
|
5
|
-
* Standard Schema for each event. Handler errors are caught and logged so a
|
|
6
|
-
* misbehaving subscriber can never crash the emitter.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { randomUUID } from "node:crypto";
|
|
10
|
-
import { EventEmitter } from "node:events";
|
|
11
|
-
|
|
12
|
-
import {
|
|
13
|
-
import
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
function getBus(): BusState {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
emitter
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
bus.subscriptions.clear();
|
|
140
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Typed pub/sub bus for framework events.
|
|
3
|
+
*
|
|
4
|
+
* Wraps Node's EventEmitter with payload validation against the registered
|
|
5
|
+
* Standard Schema for each event. Handler errors are caught and logged so a
|
|
6
|
+
* misbehaving subscriber can never crash the emitter.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { randomUUID } from "node:crypto";
|
|
10
|
+
import { EventEmitter } from "node:events";
|
|
11
|
+
|
|
12
|
+
import { getScopedGlobal } from "../shared/global-scope.js";
|
|
13
|
+
import { getEvent } from "./registry.js";
|
|
14
|
+
import type { EventMeta } from "./types.js";
|
|
15
|
+
|
|
16
|
+
type Handler = (payload: unknown, meta: EventMeta) => void | Promise<void>;
|
|
17
|
+
|
|
18
|
+
interface BusState {
|
|
19
|
+
emitter: EventEmitter;
|
|
20
|
+
subscriptions: Map<string, { event: string; handler: Handler }>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// globalThis-pinned so one app's ESM graphs share one bus, but scope-aware +
|
|
24
|
+
// lazily resolved so unified workspace deployments (all apps in one isolate)
|
|
25
|
+
// keep per-app buses — one app's handlers never fire for a same-named event
|
|
26
|
+
// emitted by a sibling app. See shared/global-scope.
|
|
27
|
+
function getBus(): BusState {
|
|
28
|
+
return getScopedGlobal("agent-native.event-bus.bus", () => {
|
|
29
|
+
const emitter = new EventEmitter();
|
|
30
|
+
// Many integrations may subscribe to the same event; lift the warning
|
|
31
|
+
// ceiling rather than printing MaxListenersExceededWarning at runtime.
|
|
32
|
+
emitter.setMaxListeners(0);
|
|
33
|
+
return { emitter, subscriptions: new Map() };
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function subscribe(event: string, handler: Handler): string {
|
|
38
|
+
if (typeof event !== "string" || !event) {
|
|
39
|
+
throw new Error("subscribe: event name is required");
|
|
40
|
+
}
|
|
41
|
+
if (typeof handler !== "function") {
|
|
42
|
+
throw new Error("subscribe: handler must be a function");
|
|
43
|
+
}
|
|
44
|
+
const bus = getBus();
|
|
45
|
+
const id = randomUUID();
|
|
46
|
+
bus.subscriptions.set(id, { event, handler });
|
|
47
|
+
bus.emitter.on(event, handler);
|
|
48
|
+
return id;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function unsubscribe(id: string): boolean {
|
|
52
|
+
const bus = getBus();
|
|
53
|
+
const sub = bus.subscriptions.get(id);
|
|
54
|
+
if (!sub) return false;
|
|
55
|
+
bus.emitter.off(sub.event, sub.handler);
|
|
56
|
+
bus.subscriptions.delete(id);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function emit(
|
|
61
|
+
event: string,
|
|
62
|
+
payload: unknown,
|
|
63
|
+
meta?: Partial<EventMeta>,
|
|
64
|
+
): void {
|
|
65
|
+
if (typeof event !== "string" || !event) {
|
|
66
|
+
throw new Error("emit: event name is required");
|
|
67
|
+
}
|
|
68
|
+
const bus = getBus();
|
|
69
|
+
const def = getEvent(event);
|
|
70
|
+
|
|
71
|
+
let validated: unknown = payload;
|
|
72
|
+
if (def) {
|
|
73
|
+
const result = def.payloadSchema["~standard"].validate(payload);
|
|
74
|
+
if (result instanceof Promise) {
|
|
75
|
+
console.warn(
|
|
76
|
+
`[event-bus] Payload schema for "${event}" returned a Promise — ` +
|
|
77
|
+
`async validation is not supported. Dispatching unvalidated payload.`,
|
|
78
|
+
);
|
|
79
|
+
} else if (result.issues) {
|
|
80
|
+
console.warn(
|
|
81
|
+
`[event-bus] Payload validation failed for "${event}":`,
|
|
82
|
+
result.issues,
|
|
83
|
+
);
|
|
84
|
+
return;
|
|
85
|
+
} else {
|
|
86
|
+
validated = (result as { value: unknown }).value;
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
console.warn(
|
|
90
|
+
`[event-bus] Emitting unregistered event "${event}". ` +
|
|
91
|
+
`Call registerEvent() to declare it.`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const fullMeta: EventMeta = {
|
|
96
|
+
eventId: meta?.eventId ?? randomUUID(),
|
|
97
|
+
emittedAt: meta?.emittedAt ?? new Date().toISOString(),
|
|
98
|
+
owner: meta?.owner,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Snapshot listeners so a handler that subscribes/unsubscribes during
|
|
102
|
+
// dispatch doesn't perturb this emission.
|
|
103
|
+
const listeners = bus.emitter.listeners(event) as Handler[];
|
|
104
|
+
for (const listener of listeners) {
|
|
105
|
+
try {
|
|
106
|
+
const r = listener(validated, fullMeta);
|
|
107
|
+
if (r && typeof (r as Promise<void>).catch === "function") {
|
|
108
|
+
(r as Promise<void>).catch((err) => {
|
|
109
|
+
console.error(
|
|
110
|
+
`[event-bus] Async handler for "${event}" rejected:`,
|
|
111
|
+
err,
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
} catch (err) {
|
|
116
|
+
console.error(`[event-bus] Handler for "${event}" threw:`, err);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function listSubscriptions(
|
|
122
|
+
event?: string,
|
|
123
|
+
): { id: string; event: string }[] {
|
|
124
|
+
const bus = getBus();
|
|
125
|
+
const out: { id: string; event: string }[] = [];
|
|
126
|
+
for (const [id, sub] of bus.subscriptions) {
|
|
127
|
+
if (event && sub.event !== event) continue;
|
|
128
|
+
out.push({ id, event: sub.event });
|
|
129
|
+
}
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Test helper — drops all subscriptions. */
|
|
134
|
+
export function __resetEventBus(): void {
|
|
135
|
+
const bus = getBus();
|
|
136
|
+
bus.emitter.removeAllListeners();
|
|
137
|
+
bus.subscriptions.clear();
|
|
138
|
+
}
|
|
@@ -1,79 +1,81 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* In-process registry of event definitions.
|
|
3
|
-
*
|
|
4
|
-
* Integrations and templates call `registerEvent()` at module load to declare
|
|
5
|
-
* the event types they emit. The bus uses these definitions to validate
|
|
6
|
-
* payloads, and the Automations UI lists them so users can build triggers.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { z } from "zod";
|
|
10
|
-
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
1
|
+
/**
|
|
2
|
+
* In-process registry of event definitions.
|
|
3
|
+
*
|
|
4
|
+
* Integrations and templates call `registerEvent()` at module load to declare
|
|
5
|
+
* the event types they emit. The bus uses these definitions to validate
|
|
6
|
+
* payloads, and the Automations UI lists them so users can build triggers.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
|
|
11
|
+
import { getScopedGlobal } from "../shared/global-scope.js";
|
|
12
|
+
import type { EventDefinition } from "./types.js";
|
|
13
|
+
|
|
14
|
+
// Pin to globalThis so multiple ESM graphs (dev-mode Vite + Nitro, symlinks,
|
|
15
|
+
// dist/ vs src/) share a single registry. Same pattern as secrets/register.ts.
|
|
16
|
+
// Scope-aware + lazily resolved so unified workspace deployments (all apps in
|
|
17
|
+
// one isolate) keep per-app event definitions. See shared/global-scope.
|
|
18
|
+
function getEventRegistry(): Map<string, EventDefinition> {
|
|
19
|
+
return getScopedGlobal(
|
|
20
|
+
"agent-native.event-bus.registry",
|
|
21
|
+
() => new Map<string, EventDefinition>(),
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Register (or replace) an event definition.
|
|
27
|
+
*
|
|
28
|
+
* Subsequent registrations with the same `name` replace the previous
|
|
29
|
+
* definition — later plugins can override built-in defaults.
|
|
30
|
+
*/
|
|
31
|
+
export function registerEvent(def: EventDefinition): void {
|
|
32
|
+
if (!def || typeof def.name !== "string" || !def.name) {
|
|
33
|
+
throw new Error("registerEvent: def.name is required");
|
|
34
|
+
}
|
|
35
|
+
if (typeof def.description !== "string" || !def.description) {
|
|
36
|
+
throw new Error("registerEvent: def.description is required");
|
|
37
|
+
}
|
|
38
|
+
if (!def.payloadSchema) {
|
|
39
|
+
throw new Error("registerEvent: def.payloadSchema is required");
|
|
40
|
+
}
|
|
41
|
+
getEventRegistry().set(def.name, def);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Return all registered events in registration order. */
|
|
45
|
+
export function listEvents(): EventDefinition[] {
|
|
46
|
+
return Array.from(getEventRegistry().values());
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Look up a single registered event by name. */
|
|
50
|
+
export function getEvent(name: string): EventDefinition | undefined {
|
|
51
|
+
return getEventRegistry().get(name);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Test helper — clears the registry between runs. */
|
|
55
|
+
export function __resetEventRegistry(): void {
|
|
56
|
+
getEventRegistry().clear();
|
|
57
|
+
registerBuiltInEvents();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function registerBuiltInEvents(): void {
|
|
61
|
+
registerEvent({
|
|
62
|
+
name: "test.event.fired",
|
|
63
|
+
description:
|
|
64
|
+
"Developer test event — fired manually from the Automations UI or via the test-event action.",
|
|
65
|
+
payloadSchema: z
|
|
66
|
+
.object({ data: z.record(z.string(), z.unknown()).optional() })
|
|
67
|
+
.optional() as unknown as EventDefinition["payloadSchema"],
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
registerEvent({
|
|
71
|
+
name: "agent.turn.completed",
|
|
72
|
+
description: "Fires after the agent completes a conversational turn.",
|
|
73
|
+
payloadSchema: z.object({
|
|
74
|
+
threadId: z.string().optional(),
|
|
75
|
+
turnIndex: z.number().optional(),
|
|
76
|
+
model: z.string().optional(),
|
|
77
|
+
}) as unknown as EventDefinition["payloadSchema"],
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
registerBuiltInEvents();
|