@dxos/compute 0.8.4-main.fcfe5033a5 → 0.8.4-staging.60fe92afc8
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/LICENSE +102 -5
- package/README.md +36 -1
- package/dist/lib/neutral/StorageService.mjs +22 -0
- package/dist/lib/neutral/StorageService.mjs.map +7 -0
- package/dist/lib/neutral/chunk-2B2KSJT6.mjs +17 -0
- package/dist/lib/neutral/chunk-2B2KSJT6.mjs.map +7 -0
- package/dist/lib/neutral/chunk-AFBNVFAY.mjs +25 -0
- package/dist/lib/neutral/chunk-AFBNVFAY.mjs.map +7 -0
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +7 -0
- package/dist/lib/neutral/chunk-JAU4E4EK.mjs +53 -0
- package/dist/lib/neutral/chunk-JAU4E4EK.mjs.map +7 -0
- package/dist/lib/neutral/chunk-MEPE4HHX.mjs +21 -0
- package/dist/lib/neutral/chunk-MEPE4HHX.mjs.map +7 -0
- package/dist/lib/neutral/chunk-QZS4G6PE.mjs +240 -0
- package/dist/lib/neutral/chunk-QZS4G6PE.mjs.map +7 -0
- package/dist/lib/neutral/chunk-XEVFBI6S.mjs +62 -0
- package/dist/lib/neutral/chunk-XEVFBI6S.mjs.map +7 -0
- package/dist/lib/neutral/chunk-Z3Y7FQGN.mjs +174 -0
- package/dist/lib/neutral/chunk-Z3Y7FQGN.mjs.map +7 -0
- package/dist/lib/neutral/fib-HBDUGQW7.mjs +10 -0
- package/dist/lib/neutral/fib-HBDUGQW7.mjs.map +7 -0
- package/dist/lib/neutral/index.mjs +1179 -51
- package/dist/lib/neutral/index.mjs.map +4 -4
- package/dist/lib/neutral/meta.json +1 -1
- package/dist/lib/neutral/reply-RJIAJ6FV.mjs +10 -0
- package/dist/lib/neutral/reply-RJIAJ6FV.mjs.map +7 -0
- package/dist/lib/neutral/sleep-3T2CZ7RE.mjs +10 -0
- package/dist/lib/neutral/sleep-3T2CZ7RE.mjs.map +7 -0
- package/dist/lib/neutral/testing/index.mjs +27 -214
- package/dist/lib/neutral/testing/index.mjs.map +4 -4
- package/dist/types/src/Blueprint.d.ts +182 -0
- package/dist/types/src/Blueprint.d.ts.map +1 -0
- package/dist/types/src/Credential.d.ts +27 -0
- package/dist/types/src/Credential.d.ts.map +1 -0
- package/dist/types/src/Header.d.ts +9 -0
- package/dist/types/src/Header.d.ts.map +1 -0
- package/dist/types/src/Header.test.d.ts +2 -0
- package/dist/types/src/Header.test.d.ts.map +1 -0
- package/dist/types/src/LayerSpec.d.ts +56 -0
- package/dist/types/src/LayerSpec.d.ts.map +1 -0
- package/dist/types/src/McpServer.d.ts +26 -0
- package/dist/types/src/McpServer.d.ts.map +1 -0
- package/dist/types/src/Operation.d.ts +385 -0
- package/dist/types/src/Operation.d.ts.map +1 -0
- package/dist/types/src/OperationHandlerSet.d.ts +69 -0
- package/dist/types/src/OperationHandlerSet.d.ts.map +1 -0
- package/dist/types/src/OperationHandlerSet.test.d.ts +2 -0
- package/dist/types/src/OperationHandlerSet.test.d.ts.map +1 -0
- package/dist/types/src/OperationRegistry.d.ts +23 -0
- package/dist/types/src/OperationRegistry.d.ts.map +1 -0
- package/dist/types/src/Process.d.ts +255 -0
- package/dist/types/src/Process.d.ts.map +1 -0
- package/dist/types/src/Routine.d.ts +54 -0
- package/dist/types/src/Routine.d.ts.map +1 -0
- package/dist/types/src/Script.d.ts +21 -0
- package/dist/types/src/Script.d.ts.map +1 -0
- package/dist/types/src/ServiceResolver.d.ts +92 -0
- package/dist/types/src/ServiceResolver.d.ts.map +1 -0
- package/dist/types/src/StorageService.d.ts +58 -0
- package/dist/types/src/StorageService.d.ts.map +1 -0
- package/dist/types/src/Template.d.ts +61 -0
- package/dist/types/src/Template.d.ts.map +1 -0
- package/dist/types/src/Template.test.d.ts +2 -0
- package/dist/types/src/Template.test.d.ts.map +1 -0
- package/dist/types/src/Trace.d.ts +266 -0
- package/dist/types/src/Trace.d.ts.map +1 -0
- package/dist/types/src/Trigger.d.ts +192 -0
- package/dist/types/src/Trigger.d.ts.map +1 -0
- package/dist/types/src/TriggerEvent.d.ts +83 -0
- package/dist/types/src/TriggerEvent.d.ts.map +1 -0
- package/dist/types/src/errors.d.ts +308 -0
- package/dist/types/src/errors.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +18 -5
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/testing/definitions.d.ts +11 -0
- package/dist/types/src/testing/definitions.d.ts.map +1 -0
- package/dist/types/src/testing/fib.d.ts +8 -0
- package/dist/types/src/testing/fib.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +6 -4
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/reply.d.ts +4 -0
- package/dist/types/src/testing/reply.d.ts.map +1 -0
- package/dist/types/src/testing/sleep.d.ts +6 -0
- package/dist/types/src/testing/sleep.d.ts.map +1 -0
- package/dist/types/src/umbrella.test.d.ts +2 -0
- package/dist/types/src/umbrella.test.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +25 -36
- package/src/Blueprint.ts +224 -0
- package/src/Credential.ts +53 -0
- package/src/Header.test.ts +132 -0
- package/src/Header.ts +38 -0
- package/src/LayerSpec.ts +91 -0
- package/src/McpServer.ts +44 -0
- package/src/Operation.ts +646 -0
- package/src/OperationHandlerSet.test.ts +97 -0
- package/src/OperationHandlerSet.ts +181 -0
- package/src/OperationRegistry.ts +50 -0
- package/src/Process.ts +541 -0
- package/src/Routine.ts +68 -0
- package/src/Script.ts +33 -0
- package/src/ServiceResolver.ts +200 -0
- package/src/StorageService.ts +101 -0
- package/src/Template.test.ts +164 -0
- package/src/Template.ts +125 -0
- package/src/Trace.ts +363 -0
- package/src/Trigger.ts +196 -0
- package/src/TriggerEvent.ts +64 -0
- package/src/errors.ts +71 -0
- package/src/index.ts +20 -6
- package/src/testing/definitions.ts +51 -0
- package/src/testing/fib.ts +22 -0
- package/src/testing/index.ts +13 -5
- package/src/testing/reply.ts +18 -0
- package/src/testing/sleep.ts +16 -0
- package/src/umbrella.test.ts +51 -0
- package/dist/lib/neutral/chunk-JRV42MW3.mjs +0 -3101
- package/dist/lib/neutral/chunk-JRV42MW3.mjs.map +0 -7
- package/dist/types/src/compute-graph-registry.d.ts +0 -37
- package/dist/types/src/compute-graph-registry.d.ts.map +0 -1
- package/dist/types/src/compute-graph-registry.test.d.ts +0 -2
- package/dist/types/src/compute-graph-registry.test.d.ts.map +0 -1
- package/dist/types/src/compute-graph.d.ts +0 -67
- package/dist/types/src/compute-graph.d.ts.map +0 -1
- package/dist/types/src/compute-graph.test.d.ts +0 -2
- package/dist/types/src/compute-graph.test.d.ts.map +0 -1
- package/dist/types/src/compute-node.d.ts +0 -24
- package/dist/types/src/compute-node.d.ts.map +0 -1
- package/dist/types/src/functions/async-function.d.ts +0 -68
- package/dist/types/src/functions/async-function.d.ts.map +0 -1
- package/dist/types/src/functions/edge-function.d.ts +0 -20
- package/dist/types/src/functions/edge-function.d.ts.map +0 -1
- package/dist/types/src/functions/function-defs.d.ts +0 -11
- package/dist/types/src/functions/function-defs.d.ts.map +0 -1
- package/dist/types/src/functions/index.d.ts +0 -4
- package/dist/types/src/functions/index.d.ts.map +0 -1
- package/dist/types/src/hyperformula.test.d.ts +0 -2
- package/dist/types/src/hyperformula.test.d.ts.map +0 -1
- package/dist/types/src/testing/test-builder.d.ts +0 -15
- package/dist/types/src/testing/test-builder.d.ts.map +0 -1
- package/dist/types/src/testing/test-plugin.d.ts +0 -35
- package/dist/types/src/testing/test-plugin.d.ts.map +0 -1
- package/dist/types/src/testing/test-runtime.d.ts +0 -10
- package/dist/types/src/testing/test-runtime.d.ts.map +0 -1
- package/dist/types/src/testing/util.d.ts +0 -2
- package/dist/types/src/testing/util.d.ts.map +0 -1
- package/dist/types/src/types/hyperformula.d.ts +0 -8
- package/dist/types/src/types/hyperformula.d.ts.map +0 -1
- package/dist/types/src/types/index.d.ts +0 -3
- package/dist/types/src/types/index.d.ts.map +0 -1
- package/dist/types/src/types/types.d.ts +0 -20
- package/dist/types/src/types/types.d.ts.map +0 -1
- package/dist/types/src/types/types.test.d.ts +0 -2
- package/dist/types/src/types/types.test.d.ts.map +0 -1
- package/dist/types/src/util.d.ts +0 -2
- package/dist/types/src/util.d.ts.map +0 -1
- package/src/compute-graph-registry.test.ts +0 -167
- package/src/compute-graph-registry.ts +0 -97
- package/src/compute-graph.test.ts +0 -139
- package/src/compute-graph.ts +0 -265
- package/src/compute-node.ts +0 -59
- package/src/functions/async-function.ts +0 -184
- package/src/functions/edge-function.ts +0 -108
- package/src/functions/function-defs.ts +0 -2427
- package/src/functions/index.ts +0 -7
- package/src/hyperformula.test.ts +0 -14
- package/src/testing/test-builder.ts +0 -59
- package/src/testing/test-plugin.ts +0 -98
- package/src/testing/test-runtime.ts +0 -53
- package/src/testing/util.ts +0 -9
- package/src/types/hyperformula.ts +0 -19
- package/src/types/index.ts +0 -6
- package/src/types/types.test.ts +0 -91
- package/src/types/types.ts +0 -80
- package/src/util.ts +0 -8
package/src/Trace.ts
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// @import-as-namespace
|
|
6
|
+
|
|
7
|
+
import * as Context from 'effect/Context';
|
|
8
|
+
import * as Effect from 'effect/Effect';
|
|
9
|
+
import * as Layer from 'effect/Layer';
|
|
10
|
+
import * as Schema from 'effect/Schema';
|
|
11
|
+
|
|
12
|
+
import { DXN, Annotation, Obj, Type } from '@dxos/echo';
|
|
13
|
+
import { log } from '@dxos/log';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Writes ephemeral or persistent events to the trace.
|
|
17
|
+
* Exposed to processes and operations to record events to the trace.
|
|
18
|
+
*/
|
|
19
|
+
export interface TraceWriter {
|
|
20
|
+
write<T>(eventType: EventType<T>, payload: NoInfer<T>): void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Service that writes events to the trace.
|
|
25
|
+
* Exposed to processes and operations to record events to the trace.
|
|
26
|
+
*/
|
|
27
|
+
export class TraceService extends Context.Tag('@dxos/functions/TraceService')<TraceService, TraceWriter>() {}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Writes an event to the trace.
|
|
31
|
+
*/
|
|
32
|
+
export function write<T>(eventType: EventType<T>, payload: NoInfer<T>): Effect.Effect<void, never, TraceService> {
|
|
33
|
+
return Effect.gen(function* () {
|
|
34
|
+
log('trace write', { key: eventType.key, isEphemeral: eventType.isEphemeral });
|
|
35
|
+
const writer = yield* TraceService;
|
|
36
|
+
writer.write(eventType, payload);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Defines an event type for the trace.
|
|
42
|
+
*/
|
|
43
|
+
export interface EventType<T> {
|
|
44
|
+
readonly key: string;
|
|
45
|
+
readonly schema: Schema.Schema<T, any>;
|
|
46
|
+
readonly isEphemeral: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const EventType = <T>(
|
|
50
|
+
key: string,
|
|
51
|
+
opts: { schema: Schema.Schema<T, any>; isEphemeral: boolean },
|
|
52
|
+
): EventType<T> => {
|
|
53
|
+
return {
|
|
54
|
+
key,
|
|
55
|
+
schema: opts.schema,
|
|
56
|
+
isEphemeral: opts.isEphemeral,
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Extracts the payload type from an event type.
|
|
62
|
+
*/
|
|
63
|
+
export type PayloadType<E extends EventType<any>> = E extends EventType<infer T> ? T : never;
|
|
64
|
+
|
|
65
|
+
export const Event = Schema.Struct({
|
|
66
|
+
timestamp: Schema.Number,
|
|
67
|
+
type: Schema.String,
|
|
68
|
+
data: Schema.Unknown, // Type-specific payload;
|
|
69
|
+
});
|
|
70
|
+
export type Event = Schema.Schema.Type<typeof Event>;
|
|
71
|
+
/**
|
|
72
|
+
* Checks if an event is of a given type.
|
|
73
|
+
*/
|
|
74
|
+
export const isOfType = <T, E extends Event>(eventType: EventType<T>, event: E): event is E & { data: T } => {
|
|
75
|
+
return event.type === eventType.key;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Extensible name informing which runtime executed the code that produced the event.
|
|
80
|
+
*/
|
|
81
|
+
export const RuntimeName = Schema.String.pipe(Schema.brand('@dxos/compute/Trace/RuntimeName'));
|
|
82
|
+
export type RuntimeName = Schema.Schema.Type<typeof RuntimeName>;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Common runtime names.
|
|
86
|
+
*/
|
|
87
|
+
export const CommonRuntimeName = {
|
|
88
|
+
/**
|
|
89
|
+
* Web app / CLI / Desktop app / Mobile app.
|
|
90
|
+
*/
|
|
91
|
+
local: RuntimeName.make('local'),
|
|
92
|
+
edgeIntrinsic: RuntimeName.make('edge-intrinsic'),
|
|
93
|
+
edgeWorkerLoader: RuntimeName.make('edge-worker-loader'),
|
|
94
|
+
edgeWorkerForPlatforms: RuntimeName.make('edge-worker-for-platforms'),
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Metadata on the context of a trace message.
|
|
99
|
+
*/
|
|
100
|
+
// TODO(dmaretskyi): Expand on this: conversation id, tool call id, etc.
|
|
101
|
+
export const Meta = Schema.Struct({
|
|
102
|
+
pid: Schema.optional(Schema.String), // NOTE: Not Process.ID to avoid circular dependency.
|
|
103
|
+
parentPid: Schema.optional(Schema.String),
|
|
104
|
+
processName: Schema.optional(Schema.String),
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Space the message was produced in.
|
|
108
|
+
* Stored as a string to avoid circular dependency on `@dxos/keys`.
|
|
109
|
+
*/
|
|
110
|
+
space: Schema.optional(Schema.String),
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* ID of the conversation feed object if present.
|
|
114
|
+
*/
|
|
115
|
+
conversationId: Schema.optional(Obj.ID),
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* ID of the trigger object if invocation resulted from a trigger.
|
|
119
|
+
*/
|
|
120
|
+
triggerId: Schema.optional(Obj.ID),
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* ID of the tool call that created the current process.
|
|
124
|
+
*/
|
|
125
|
+
toolCallId: Schema.optional(Schema.String),
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Extensible name informing which runtime executed the code that produced the event.
|
|
129
|
+
*/
|
|
130
|
+
runtimeName: Schema.optional(RuntimeName),
|
|
131
|
+
});
|
|
132
|
+
export type Meta = Schema.Schema.Type<typeof Meta>;
|
|
133
|
+
/**
|
|
134
|
+
* Checks if a runtime is an edge runtime.
|
|
135
|
+
*/
|
|
136
|
+
export const isEdgeRuntime = (name: RuntimeName): boolean =>
|
|
137
|
+
name === CommonRuntimeName.edgeIntrinsic ||
|
|
138
|
+
name === CommonRuntimeName.edgeWorkerLoader ||
|
|
139
|
+
name === CommonRuntimeName.edgeWorkerForPlatforms;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Envelope for a set of events.
|
|
143
|
+
*/
|
|
144
|
+
export const MessageData = Schema.Struct({
|
|
145
|
+
meta: Meta,
|
|
146
|
+
|
|
147
|
+
isEphemeral: Schema.Boolean,
|
|
148
|
+
events: Schema.Array(Event),
|
|
149
|
+
});
|
|
150
|
+
export type MessageData = Schema.Schema.Type<typeof MessageData>;
|
|
151
|
+
export const Message = MessageData.pipe(
|
|
152
|
+
Annotation.IconAnnotation.set({ icon: 'ph--note--regular', hue: 'rose' }),
|
|
153
|
+
Annotation.HiddenAnnotation.set(true),
|
|
154
|
+
Type.makeObject(DXN.make('org.dxos.type.traceMessage', '0.1.0')),
|
|
155
|
+
);
|
|
156
|
+
export type Message = Type.InstanceType<typeof Message>;
|
|
157
|
+
/**
|
|
158
|
+
* Flattened representation of a signle event in a trace message.
|
|
159
|
+
* Events are stored in batched messages for efficiency, but flat representation is more convenient for consumption.
|
|
160
|
+
*/
|
|
161
|
+
export interface FlatEvent extends Event {
|
|
162
|
+
readonly meta: Meta;
|
|
163
|
+
readonly isEphemeral: boolean;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Flattens a trace message into a list of flat events.
|
|
168
|
+
*/
|
|
169
|
+
export const flatten = (message: Message): FlatEvent[] => {
|
|
170
|
+
return message.events.map((event) => ({
|
|
171
|
+
...event,
|
|
172
|
+
meta: message.meta,
|
|
173
|
+
isEphemeral: message.isEphemeral,
|
|
174
|
+
}));
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Sink for complete trace messages.
|
|
179
|
+
*/
|
|
180
|
+
export interface Sink {
|
|
181
|
+
write(message: Message): void;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Sink for complete trace messages.
|
|
186
|
+
* The Process Manager forwards trace messages to it.
|
|
187
|
+
*/
|
|
188
|
+
// TODO(dmaretskyi): Consider moving sink to the Process Manager.
|
|
189
|
+
export class TraceSink extends Context.Tag('@dxos/functions/TraceSink')<TraceSink, Sink>() {}
|
|
190
|
+
|
|
191
|
+
export const noopWriter: TraceWriter = {
|
|
192
|
+
write: () => {},
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export const writerLayerNoop: Layer.Layer<TraceService> = Layer.succeed(TraceService, noopWriter);
|
|
196
|
+
|
|
197
|
+
export const noopSink: Sink = {
|
|
198
|
+
write: () => {},
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
export const layerNoop: Layer.Layer<TraceSink> = Layer.succeed(TraceSink, noopSink);
|
|
202
|
+
|
|
203
|
+
export const layerConsole: Layer.Layer<TraceSink> = Layer.succeed(TraceSink, {
|
|
204
|
+
write: (message) => {
|
|
205
|
+
console.log(message);
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Merge a set of sinks into a single sink.
|
|
211
|
+
*
|
|
212
|
+
* Each message is forwarded to every sink in order. Failures in one sink do not
|
|
213
|
+
* prevent downstream sinks from receiving the message.
|
|
214
|
+
*/
|
|
215
|
+
export const mergeSinks = (sinks: readonly Sink[]): Sink => {
|
|
216
|
+
if (sinks.length === 0) {
|
|
217
|
+
return noopSink;
|
|
218
|
+
}
|
|
219
|
+
// Intentionally no singleton fast path: the guarded wrapper is the
|
|
220
|
+
// contract of `mergeSinks`, so a throwing sink is always caught and
|
|
221
|
+
// logged regardless of how many sinks were passed in.
|
|
222
|
+
return {
|
|
223
|
+
write: (message) => {
|
|
224
|
+
for (const sink of sinks) {
|
|
225
|
+
try {
|
|
226
|
+
sink.write(message);
|
|
227
|
+
} catch (err) {
|
|
228
|
+
// Intentional: do not let one sink break the chain.
|
|
229
|
+
// eslint-disable-next-line no-console
|
|
230
|
+
console.error('[trace] sink.write threw', err);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Builds a {@link TraceService} layer that wraps each `write` into a fresh {@link Message}
|
|
239
|
+
* and forwards it to the provided {@link TraceSink}. Intended for tests and lightweight
|
|
240
|
+
* fixtures.
|
|
241
|
+
*
|
|
242
|
+
* Options:
|
|
243
|
+
* - `meta`: stamped on every emitted message (defaults to `{}`).
|
|
244
|
+
* - `clock`: timestamp source (defaults to {@link Date.now}). Pass a monotonic counter
|
|
245
|
+
* in tests to make event ordering deterministic when many writes happen in the same
|
|
246
|
+
* millisecond.
|
|
247
|
+
*/
|
|
248
|
+
export const testTraceService = (
|
|
249
|
+
opts: { meta?: Meta; clock?: () => number } = {},
|
|
250
|
+
): Layer.Layer<TraceService, never, TraceSink> =>
|
|
251
|
+
Layer.effect(
|
|
252
|
+
TraceService,
|
|
253
|
+
Effect.gen(function* () {
|
|
254
|
+
const sink = yield* TraceSink;
|
|
255
|
+
const clock = opts.clock ?? Date.now;
|
|
256
|
+
return {
|
|
257
|
+
write: (event, data) => {
|
|
258
|
+
sink.write(
|
|
259
|
+
Obj.make(Message, {
|
|
260
|
+
meta: opts.meta ?? {},
|
|
261
|
+
isEphemeral: event.isEphemeral,
|
|
262
|
+
events: [{ type: event.key, timestamp: clock(), data }],
|
|
263
|
+
}),
|
|
264
|
+
);
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
}),
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
//
|
|
271
|
+
// Operation Trace Events
|
|
272
|
+
//
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Outcome of an operation invocation.
|
|
276
|
+
*/
|
|
277
|
+
export type OperationOutcome = 'success' | 'failure';
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Operation invocation started.
|
|
281
|
+
*/
|
|
282
|
+
export const OperationStart = EventType('operation.start', {
|
|
283
|
+
schema: Schema.Struct({
|
|
284
|
+
/** Operation key. */
|
|
285
|
+
key: Schema.String,
|
|
286
|
+
/** Human-readable operation name. */
|
|
287
|
+
name: Schema.optional(Schema.String),
|
|
288
|
+
/** Phosphor icon identifier in `ph--<name>--<variant>` format. */
|
|
289
|
+
icon: Schema.optional(Schema.String),
|
|
290
|
+
}),
|
|
291
|
+
isEphemeral: false,
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Operation invocation ended.
|
|
296
|
+
*/
|
|
297
|
+
export const OperationEnd = EventType('operation.end', {
|
|
298
|
+
schema: Schema.Struct({
|
|
299
|
+
/** Operation key. */
|
|
300
|
+
key: Schema.String,
|
|
301
|
+
/** Human-readable operation name. */
|
|
302
|
+
name: Schema.optional(Schema.String),
|
|
303
|
+
/** Phosphor icon identifier in `ph--<name>--<variant>` format. */
|
|
304
|
+
icon: Schema.optional(Schema.String),
|
|
305
|
+
/** Outcome of the operation. */
|
|
306
|
+
outcome: Schema.Literal('success', 'failure'),
|
|
307
|
+
/** Error message if the operation failed. */
|
|
308
|
+
error: Schema.optional(Schema.String),
|
|
309
|
+
}),
|
|
310
|
+
isEphemeral: false,
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Operation input. Emitted as an ephemeral event alongside {@link OperationStart}
|
|
315
|
+
* so subscribers (such as the undo/redo history tracker) can observe the raw
|
|
316
|
+
* input payload without persisting it to long-lived sinks.
|
|
317
|
+
*/
|
|
318
|
+
export const OperationInput = EventType('operation.input', {
|
|
319
|
+
schema: Schema.Struct({
|
|
320
|
+
/** Operation key. */
|
|
321
|
+
key: Schema.String,
|
|
322
|
+
/** Human-readable operation name. */
|
|
323
|
+
name: Schema.optional(Schema.String),
|
|
324
|
+
/** Raw operation input. Shape determined by the operation definition. */
|
|
325
|
+
input: Schema.Unknown,
|
|
326
|
+
}),
|
|
327
|
+
isEphemeral: true,
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Operation output. Emitted as an ephemeral event just before
|
|
332
|
+
* {@link OperationEnd} for successful invocations so subscribers (such as the
|
|
333
|
+
* undo/redo history tracker) can observe the raw output payload without
|
|
334
|
+
* persisting it to long-lived sinks.
|
|
335
|
+
*/
|
|
336
|
+
export const OperationOutput = EventType('operation.output', {
|
|
337
|
+
schema: Schema.Struct({
|
|
338
|
+
/** Operation key. */
|
|
339
|
+
key: Schema.String,
|
|
340
|
+
/** Human-readable operation name. */
|
|
341
|
+
name: Schema.optional(Schema.String),
|
|
342
|
+
/** Raw operation output. Shape determined by the operation definition. */
|
|
343
|
+
output: Schema.Unknown,
|
|
344
|
+
}),
|
|
345
|
+
isEphemeral: true,
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Human-readable status update emitted by an agent or operation.
|
|
350
|
+
*/
|
|
351
|
+
export const StatusUpdate = EventType('status.update', {
|
|
352
|
+
schema: Schema.Struct({
|
|
353
|
+
/** Human-readable status message. */
|
|
354
|
+
message: Schema.String,
|
|
355
|
+
}),
|
|
356
|
+
isEphemeral: true,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Emit the current human-readable execution status to the trace.
|
|
361
|
+
*/
|
|
362
|
+
export const emitStatus: (message: string) => Effect.Effect<void, never, TraceService> = (message) =>
|
|
363
|
+
write(StatusUpdate, { message });
|
package/src/Trigger.ts
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// @import-as-namespace
|
|
6
|
+
|
|
7
|
+
import * as Schema from 'effect/Schema';
|
|
8
|
+
import * as SchemaAST from 'effect/SchemaAST';
|
|
9
|
+
|
|
10
|
+
import { DXN, Annotation, Feed, Obj, QueryAST, Ref, Type, type Query } from '@dxos/echo';
|
|
11
|
+
import { HiddenAnnotation } from '@dxos/echo/Annotation';
|
|
12
|
+
import { OptionsAnnotationId } from '@dxos/echo/Format';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Type discriminator for TriggerType.
|
|
16
|
+
* Every spec has a type field of type TriggerKind that we can use to understand which type we're working with.
|
|
17
|
+
* https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions
|
|
18
|
+
*/
|
|
19
|
+
export const Kinds = ['email', 'feed', 'subscription', 'timer', 'webhook'] as const;
|
|
20
|
+
export type Kind = (typeof Kinds)[number];
|
|
21
|
+
|
|
22
|
+
const kindLiteralAnnotations = { title: 'Kind' };
|
|
23
|
+
|
|
24
|
+
export const EmailSpec = Schema.Struct({
|
|
25
|
+
kind: Schema.Literal('email').annotations(kindLiteralAnnotations),
|
|
26
|
+
});
|
|
27
|
+
export type EmailSpec = Schema.Schema.Type<typeof EmailSpec>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Construct an Email trigger spec.
|
|
31
|
+
*/
|
|
32
|
+
export const specEmail = (): EmailSpec => ({ kind: 'email' });
|
|
33
|
+
|
|
34
|
+
// TODO(wittjosiah): Remove. Migrate to Subscription triggers once EDGE supports them for feed queries.
|
|
35
|
+
export const FeedSpec = Schema.Struct({
|
|
36
|
+
kind: Schema.Literal('feed').annotations(kindLiteralAnnotations),
|
|
37
|
+
feed: Schema.optional(Ref.Ref(Feed.Feed).annotations({ title: 'Feed' })),
|
|
38
|
+
});
|
|
39
|
+
export type FeedSpec = Schema.Schema.Type<typeof FeedSpec>;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Construct a Feed trigger spec from a Feed object.
|
|
43
|
+
*/
|
|
44
|
+
export const specFeed = (feed: Feed.Feed): FeedSpec => ({
|
|
45
|
+
kind: 'feed',
|
|
46
|
+
feed: Ref.make(feed),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Subscription.
|
|
51
|
+
*/
|
|
52
|
+
export const SubscriptionSpec = Schema.Struct({
|
|
53
|
+
kind: Schema.Literal('subscription').annotations(kindLiteralAnnotations),
|
|
54
|
+
query: Schema.Struct({
|
|
55
|
+
raw: Schema.optional(Schema.String.annotations({ title: 'Query' })),
|
|
56
|
+
ast: QueryAST.Query,
|
|
57
|
+
}),
|
|
58
|
+
options: Schema.optional(
|
|
59
|
+
Schema.Struct({
|
|
60
|
+
// Watch changes to object (not just creation).
|
|
61
|
+
deep: Schema.optional(Schema.Boolean.annotations({ title: 'Nested' })),
|
|
62
|
+
// Debounce changes (delay in ms).
|
|
63
|
+
delay: Schema.optional(Schema.Number.annotations({ title: 'Delay' })),
|
|
64
|
+
}).annotations({ title: 'Options' }),
|
|
65
|
+
),
|
|
66
|
+
});
|
|
67
|
+
export type SubscriptionSpec = Schema.Schema.Type<typeof SubscriptionSpec>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Construct a Subscription trigger spec from a Query object.
|
|
71
|
+
*/
|
|
72
|
+
export const specSubscription = (
|
|
73
|
+
query: Query.Query<any>,
|
|
74
|
+
options?: { deep?: boolean; delay?: number },
|
|
75
|
+
): SubscriptionSpec => ({
|
|
76
|
+
kind: 'subscription',
|
|
77
|
+
query: {
|
|
78
|
+
ast: query.ast,
|
|
79
|
+
},
|
|
80
|
+
options: options
|
|
81
|
+
? {
|
|
82
|
+
deep: options.deep,
|
|
83
|
+
delay: options.delay,
|
|
84
|
+
}
|
|
85
|
+
: undefined,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Cron timer.
|
|
90
|
+
*/
|
|
91
|
+
export const TimerSpec = Schema.Struct({
|
|
92
|
+
kind: Schema.Literal('timer').annotations(kindLiteralAnnotations),
|
|
93
|
+
cron: Schema.String.annotations({
|
|
94
|
+
title: 'Cron',
|
|
95
|
+
[SchemaAST.ExamplesAnnotationId]: ['0 0 * * *'],
|
|
96
|
+
}),
|
|
97
|
+
});
|
|
98
|
+
export type TimerSpec = Schema.Schema.Type<typeof TimerSpec>;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Construct a Timer trigger spec from a cron string.
|
|
102
|
+
*/
|
|
103
|
+
export const specTimer = (cron: string): TimerSpec => ({ kind: 'timer', cron });
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Webhook.
|
|
107
|
+
*/
|
|
108
|
+
export const WebhookSpec = Schema.Struct({
|
|
109
|
+
kind: Schema.Literal('webhook').annotations(kindLiteralAnnotations),
|
|
110
|
+
method: Schema.optional(
|
|
111
|
+
Schema.String.annotations({
|
|
112
|
+
title: 'Method',
|
|
113
|
+
[OptionsAnnotationId]: ['GET', 'POST'],
|
|
114
|
+
}),
|
|
115
|
+
),
|
|
116
|
+
port: Schema.optional(
|
|
117
|
+
Schema.Number.annotations({
|
|
118
|
+
title: 'Port',
|
|
119
|
+
}),
|
|
120
|
+
),
|
|
121
|
+
});
|
|
122
|
+
export type WebhookSpec = Schema.Schema.Type<typeof WebhookSpec>;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Construct a Webhook trigger spec from a method and port.
|
|
126
|
+
*/
|
|
127
|
+
export const specWebhook = (opts?: { method?: string; port?: number }): WebhookSpec => ({
|
|
128
|
+
kind: 'webhook',
|
|
129
|
+
method: opts?.method,
|
|
130
|
+
port: opts?.port,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Trigger schema.
|
|
135
|
+
*/
|
|
136
|
+
export const Spec = Schema.Union(EmailSpec, FeedSpec, SubscriptionSpec, TimerSpec, WebhookSpec).annotations({
|
|
137
|
+
title: 'Trigger',
|
|
138
|
+
});
|
|
139
|
+
export type Spec = Schema.Schema.Type<typeof Spec>;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Function trigger.
|
|
143
|
+
* Function is invoked with the `payload` passed as input data.
|
|
144
|
+
* The event that triggers the function is available in the function context.
|
|
145
|
+
*/
|
|
146
|
+
const TriggerSchema = Schema.Struct({
|
|
147
|
+
/**
|
|
148
|
+
* Function or workflow to invoke.
|
|
149
|
+
*/
|
|
150
|
+
// TODO(dmaretskyi): Can be a Ref(FunctionType) or Ref(ComputeGraphType).
|
|
151
|
+
function: Schema.optional(Ref.Ref(Obj.Unknown).annotations({ title: 'Function' })),
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Only used for workflowSchema.
|
|
155
|
+
* Specifies the input node in the circuit.
|
|
156
|
+
* @deprecated Remove and enforce a single input node in all compute graphSchema.
|
|
157
|
+
*/
|
|
158
|
+
inputNodeId: Schema.optional(Schema.String.annotations({ title: 'Input Node ID' })),
|
|
159
|
+
|
|
160
|
+
// TODO(burdon): NO BOOLEAN PROPERTIES (enabld/disabled/paused, etc.)
|
|
161
|
+
// Need lint rule; or agent rule to require PR review for "boolean" key word.
|
|
162
|
+
enabled: Schema.optional(Schema.Boolean.annotations({ title: 'Enabled' })),
|
|
163
|
+
|
|
164
|
+
spec: Schema.optional(Spec),
|
|
165
|
+
|
|
166
|
+
concurrency: Schema.optional(
|
|
167
|
+
Schema.Number.annotations({
|
|
168
|
+
title: 'Concurrency',
|
|
169
|
+
default: 1,
|
|
170
|
+
description:
|
|
171
|
+
'Maximum number of concurrent invocations of the trigger. For Feed triggers, this will process Feed items in parallel.',
|
|
172
|
+
}),
|
|
173
|
+
),
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Passed as the input data to the function.
|
|
177
|
+
* Must match the function's input schema.
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* {
|
|
181
|
+
* item: '{{event.item}}',
|
|
182
|
+
* instructions: 'Summarize and perform entity-extraction'
|
|
183
|
+
* mailbox: { '/': 'echo://AAA/ZZZ' }
|
|
184
|
+
* }
|
|
185
|
+
*/
|
|
186
|
+
input: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Any })),
|
|
187
|
+
}).pipe(
|
|
188
|
+
Annotation.IconAnnotation.set({ icon: 'ph--lightning--regular', hue: 'yellow' }),
|
|
189
|
+
HiddenAnnotation.set(true),
|
|
190
|
+
Type.makeObject(DXN.make('org.dxos.type.trigger', '0.1.0')),
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
export type Trigger = Type.InstanceType<typeof TriggerSchema>;
|
|
194
|
+
export const Trigger: Type.Obj<Trigger> = TriggerSchema as any;
|
|
195
|
+
|
|
196
|
+
export const make = (props: Obj.MakeProps<typeof Trigger>) => Obj.make(Trigger, props);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// @import-as-namespace
|
|
6
|
+
|
|
7
|
+
import * as Schema from 'effect/Schema';
|
|
8
|
+
|
|
9
|
+
import { Feed, Obj, Ref } from '@dxos/echo';
|
|
10
|
+
|
|
11
|
+
// TODO(wittjosiah): Review this type.
|
|
12
|
+
// - Should be discriminated union.
|
|
13
|
+
// - Should be more consistent (e.g. subject vs item).
|
|
14
|
+
// - Should re-use schemas if possible.
|
|
15
|
+
|
|
16
|
+
// TODO(burdon): Reuse trigger schema from @dxos/functions (TriggerType).
|
|
17
|
+
export const EmailEvent = Schema.Struct({
|
|
18
|
+
from: Schema.String,
|
|
19
|
+
to: Schema.String,
|
|
20
|
+
subject: Schema.String,
|
|
21
|
+
created: Schema.String,
|
|
22
|
+
body: Schema.String,
|
|
23
|
+
});
|
|
24
|
+
export type EmailEvent = Schema.Schema.Type<typeof EmailEvent>;
|
|
25
|
+
|
|
26
|
+
export const FeedEvent = Schema.Struct({
|
|
27
|
+
feed: Ref.Ref(Feed.Feed),
|
|
28
|
+
item: Schema.Any,
|
|
29
|
+
cursor: Schema.String,
|
|
30
|
+
});
|
|
31
|
+
export type FeedEvent = Schema.Schema.Type<typeof FeedEvent>;
|
|
32
|
+
|
|
33
|
+
export const SubscriptionEvent = Schema.Struct({
|
|
34
|
+
/**
|
|
35
|
+
* Type of the mutation.
|
|
36
|
+
*/
|
|
37
|
+
// TODO(dmaretskyi): Specify enum.
|
|
38
|
+
type: Schema.String,
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Reference to the object that was changed or created.
|
|
42
|
+
*/
|
|
43
|
+
subject: Ref.Ref(Obj.Unknown),
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @deprecated
|
|
47
|
+
*/
|
|
48
|
+
changedObjectId: Schema.optional(Schema.String),
|
|
49
|
+
});
|
|
50
|
+
export type SubscriptionEvent = Schema.Schema.Type<typeof SubscriptionEvent>;
|
|
51
|
+
|
|
52
|
+
export const TimerEvent = Schema.Struct({ tick: Schema.Number });
|
|
53
|
+
export type TimerEvent = Schema.Schema.Type<typeof TimerEvent>;
|
|
54
|
+
|
|
55
|
+
export const WebhookEvent = Schema.Struct({
|
|
56
|
+
url: Schema.String,
|
|
57
|
+
method: Schema.Literal('GET', 'POST'),
|
|
58
|
+
headers: Schema.Record({ key: Schema.String, value: Schema.String }),
|
|
59
|
+
bodyText: Schema.String,
|
|
60
|
+
});
|
|
61
|
+
export type WebhookEvent = Schema.Schema.Type<typeof WebhookEvent>;
|
|
62
|
+
|
|
63
|
+
export const TriggerEvent = Schema.Union(EmailEvent, FeedEvent, SubscriptionEvent, TimerEvent, WebhookEvent);
|
|
64
|
+
export type TriggerEvent = Schema.Schema.Type<typeof TriggerEvent>;
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { BaseError, type BaseErrorOptions } from '@dxos/errors';
|
|
6
|
+
|
|
7
|
+
// Errors from @dxos/operation.
|
|
8
|
+
|
|
9
|
+
export class InvokerNotInitializedError extends BaseError.extend(
|
|
10
|
+
'InvokerNotInitializedError',
|
|
11
|
+
'Invoker not initialized',
|
|
12
|
+
) {
|
|
13
|
+
constructor() {
|
|
14
|
+
super();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class NoHandlerError extends BaseError.extend('NoHandlerError', 'No handler found for operation. ') {
|
|
19
|
+
constructor(operationKey: string) {
|
|
20
|
+
super({ context: { operationKey } });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Errors from @dxos/functions.
|
|
25
|
+
|
|
26
|
+
export class ServiceNotAvailableError extends BaseError.extend('ServiceNotAvailable', 'Service not available') {
|
|
27
|
+
constructor(service: string, options?: Omit<BaseErrorOptions, 'context'>) {
|
|
28
|
+
super({ context: { service }, message: `Service not available: ${service}`, ...options });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class FunctionNotFoundError extends BaseError.extend('FunctionNotFound', 'Function not found') {
|
|
33
|
+
constructor(functionKey: string, options?: Omit<BaseErrorOptions, 'context'>) {
|
|
34
|
+
super({ context: { function: functionKey }, ...options });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class FunctionError extends BaseError.extend('FunctionError', 'Function invocation error') {}
|
|
39
|
+
|
|
40
|
+
export class InvalidOperationInputError extends BaseError.extend(
|
|
41
|
+
'InvalidOperationInput',
|
|
42
|
+
'Operation input did not match schema',
|
|
43
|
+
) {}
|
|
44
|
+
|
|
45
|
+
export class InvalidOperationOutputError extends BaseError.extend(
|
|
46
|
+
'InvalidOperationOutput',
|
|
47
|
+
'Operation output did not match schema',
|
|
48
|
+
) {}
|
|
49
|
+
|
|
50
|
+
export class TriggerStateNotFoundError extends BaseError.extend('TriggerStateNotFound', 'Trigger state not found') {}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Raised when the upstream AI gateway responds with a structured JSON error envelope
|
|
54
|
+
* (`{ "type": "error", "error": { "type": ..., "message": ... } }`). Surfaces as a typed
|
|
55
|
+
* defect from `FunctionsAiHttpClient`, bypassing `@effect/ai`'s generic `HttpResponseError`
|
|
56
|
+
* wrapping so callers can match on the error class directly.
|
|
57
|
+
*/
|
|
58
|
+
export class FunctionsAiUpstreamError extends BaseError.extend(
|
|
59
|
+
'FunctionsAiUpstreamError',
|
|
60
|
+
'Upstream AI service returned an error',
|
|
61
|
+
) {}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Specialized `FunctionsAiUpstreamError` for the memoization layer: the recorded fixture for a
|
|
65
|
+
* given cache key was not found and `ALLOW_LLM_GENERATION` is unset. The `cacheKey` is exposed
|
|
66
|
+
* via `context.cacheKey` to make regeneration straightforward.
|
|
67
|
+
*/
|
|
68
|
+
export class FunctionsAiMemoizationMissError extends BaseError.extend(
|
|
69
|
+
'FunctionsAiMemoizationMissError',
|
|
70
|
+
'No memoized AI conversation found for the given cache key',
|
|
71
|
+
) {}
|