@dxos/functions 0.8.4-main.bc674ce → 0.8.4-main.bcb3aa67d6

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 (95) hide show
  1. package/README.md +4 -6
  2. package/dist/lib/neutral/Trace.mjs +34 -0
  3. package/dist/lib/neutral/Trace.mjs.map +7 -0
  4. package/dist/lib/neutral/chunk-BHLSCAA2.mjs +123 -0
  5. package/dist/lib/neutral/chunk-BHLSCAA2.mjs.map +7 -0
  6. package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
  7. package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +7 -0
  8. package/dist/lib/neutral/chunk-Z2XDJJVH.mjs +49 -0
  9. package/dist/lib/neutral/chunk-Z2XDJJVH.mjs.map +7 -0
  10. package/dist/lib/neutral/fib-S6PPI4UW.mjs +23 -0
  11. package/dist/lib/neutral/fib-S6PPI4UW.mjs.map +7 -0
  12. package/dist/lib/{browser → neutral}/index.mjs +649 -633
  13. package/dist/lib/neutral/index.mjs.map +7 -0
  14. package/dist/lib/neutral/meta.json +1 -0
  15. package/dist/lib/neutral/reply-TOHXEG7V.mjs +19 -0
  16. package/dist/lib/neutral/reply-TOHXEG7V.mjs.map +7 -0
  17. package/dist/lib/neutral/sleep-QPSZDPEH.mjs +15 -0
  18. package/dist/lib/neutral/sleep-QPSZDPEH.mjs.map +7 -0
  19. package/dist/types/src/Trace.d.ts +135 -0
  20. package/dist/types/src/Trace.d.ts.map +1 -0
  21. package/dist/types/src/errors.d.ts.map +1 -1
  22. package/dist/types/src/example/definitions.d.ts +11 -0
  23. package/dist/types/src/example/definitions.d.ts.map +1 -0
  24. package/dist/types/src/example/fib.d.ts +3 -2
  25. package/dist/types/src/example/fib.d.ts.map +1 -1
  26. package/dist/types/src/example/index.d.ts +3 -11
  27. package/dist/types/src/example/index.d.ts.map +1 -1
  28. package/dist/types/src/example/reply.d.ts +2 -1
  29. package/dist/types/src/example/reply.d.ts.map +1 -1
  30. package/dist/types/src/example/sleep.d.ts +3 -2
  31. package/dist/types/src/example/sleep.d.ts.map +1 -1
  32. package/dist/types/src/index.d.ts +4 -0
  33. package/dist/types/src/index.d.ts.map +1 -1
  34. package/dist/types/src/process/Process.d.ts +247 -0
  35. package/dist/types/src/process/Process.d.ts.map +1 -0
  36. package/dist/types/src/process/ServiceResolver.d.ts +74 -0
  37. package/dist/types/src/process/ServiceResolver.d.ts.map +1 -0
  38. package/dist/types/src/process/StorageService.d.ts +58 -0
  39. package/dist/types/src/process/StorageService.d.ts.map +1 -0
  40. package/dist/types/src/protocol/protocol.d.ts +2 -2
  41. package/dist/types/src/protocol/protocol.d.ts.map +1 -1
  42. package/dist/types/src/sdk.d.ts +4 -104
  43. package/dist/types/src/sdk.d.ts.map +1 -1
  44. package/dist/types/src/services/event-logger.d.ts +4 -4
  45. package/dist/types/src/services/function-invocation-service.d.ts +6 -5
  46. package/dist/types/src/services/function-invocation-service.d.ts.map +1 -1
  47. package/dist/types/src/services/queues.d.ts +4 -2
  48. package/dist/types/src/services/queues.d.ts.map +1 -1
  49. package/dist/types/src/services/tracing.d.ts +25 -2
  50. package/dist/types/src/services/tracing.d.ts.map +1 -1
  51. package/dist/types/src/types/Script.d.ts +4 -3
  52. package/dist/types/src/types/Script.d.ts.map +1 -1
  53. package/dist/types/src/types/Trigger.d.ts +8 -9
  54. package/dist/types/src/types/Trigger.d.ts.map +1 -1
  55. package/dist/types/src/types/TriggerEvent.d.ts +3 -2
  56. package/dist/types/src/types/TriggerEvent.d.ts.map +1 -1
  57. package/dist/types/src/types/index.d.ts +0 -1
  58. package/dist/types/src/types/index.d.ts.map +1 -1
  59. package/dist/types/src/types/url.d.ts +2 -2
  60. package/dist/types/tsconfig.tsbuildinfo +1 -1
  61. package/package.json +25 -20
  62. package/src/Trace.ts +162 -0
  63. package/src/errors.ts +1 -1
  64. package/src/example/definitions.ts +48 -0
  65. package/src/example/fib.ts +14 -24
  66. package/src/example/forex-effect.ts +1 -1
  67. package/src/example/index.ts +7 -8
  68. package/src/example/reply.ts +10 -13
  69. package/src/example/sleep.ts +8 -17
  70. package/src/index.ts +4 -0
  71. package/src/process/Process.ts +457 -0
  72. package/src/process/ServiceResolver.ts +173 -0
  73. package/src/process/StorageService.ts +99 -0
  74. package/src/protocol/protocol.ts +33 -27
  75. package/src/sdk.ts +6 -256
  76. package/src/services/event-logger.ts +1 -1
  77. package/src/services/function-invocation-service.ts +6 -5
  78. package/src/services/queues.ts +10 -2
  79. package/src/services/tracing.ts +35 -2
  80. package/src/types/Script.ts +7 -3
  81. package/src/types/Trigger.ts +17 -6
  82. package/src/types/TriggerEvent.ts +2 -2
  83. package/src/types/index.ts +0 -1
  84. package/src/types/url.ts +2 -2
  85. package/dist/lib/browser/index.mjs.map +0 -7
  86. package/dist/lib/browser/meta.json +0 -1
  87. package/dist/lib/node-esm/index.mjs +0 -1230
  88. package/dist/lib/node-esm/index.mjs.map +0 -7
  89. package/dist/lib/node-esm/meta.json +0 -1
  90. package/dist/types/src/operation-compatibility.test.d.ts +0 -2
  91. package/dist/types/src/operation-compatibility.test.d.ts.map +0 -1
  92. package/dist/types/src/types/Function.d.ts +0 -52
  93. package/dist/types/src/types/Function.d.ts.map +0 -1
  94. package/src/operation-compatibility.test.ts +0 -185
  95. package/src/types/Function.ts +0 -82
@@ -0,0 +1,99 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import * as Pipeable from 'effect/Pipeable';
6
+ import * as Context from 'effect/Context';
7
+ import * as Effect from 'effect/Effect';
8
+ import * as Option from 'effect/Option';
9
+ import * as Schema from 'effect/Schema';
10
+
11
+ export interface Service {
12
+ /** Read a value by key. Returns `None` if key does not exist. */
13
+ get<S extends Schema.Schema<any, string, any>>(
14
+ schema: S,
15
+ key: string,
16
+ ): Effect.Effect<Option.Option<Schema.Schema.Type<S>>, never, Schema.Schema.Context<S>>;
17
+
18
+ /** Write a value for the given key. */
19
+ set<S extends Schema.Schema<any, string, any>>(
20
+ schema: S,
21
+ key: string,
22
+ value: Schema.Schema.Type<S>,
23
+ ): Effect.Effect<void, never, Schema.Schema.Context<S>>;
24
+
25
+ /** Remove a key. */
26
+ delete(key: string): Effect.Effect<void>;
27
+
28
+ /** List all keys, optionally filtered by prefix. */
29
+ list(prefix?: string): Effect.Effect<readonly string[]>;
30
+
31
+ /** Remove all keys managed by this scoped store. */
32
+ clear(): Effect.Effect<void>;
33
+ }
34
+
35
+ /**
36
+ * Scoped key-value storage service for processes.
37
+ * Each process receives its own namespaced instance via the process manager.
38
+ * Construct a live implementation with `StorageService.layer` from `@dxos/functions-runtime`.
39
+ */
40
+ export class StorageService extends Context.Tag('@dxos/functions/StorageService')<StorageService, Service>() {}
41
+
42
+ export const get = Effect.serviceFunctionEffect(StorageService, (_) => _.get);
43
+ export const set = Effect.serviceFunctionEffect(StorageService, (_) => _.set);
44
+ export const deleteKey = Effect.serviceFunctionEffect(StorageService, (_) => _.delete);
45
+ export const list = Effect.serviceFunctionEffect(StorageService, (_) => _.list);
46
+ export const clear = Effect.serviceFunctionEffect(StorageService, (_) => _.clear);
47
+
48
+ /**
49
+ * Typed key in a storage service.
50
+ */
51
+ export interface Key<T> extends Pipeable.Pipeable {
52
+ readonly key: string;
53
+
54
+ get: Effect.Effect<Option.Option<T>, never, StorageService>;
55
+ set(value: T): Effect.Effect<void, never, StorageService>;
56
+ delete(): Effect.Effect<void, never, StorageService>;
57
+ }
58
+
59
+ /**
60
+ * Create a typed key in a storage service.
61
+ */
62
+ export const key = <S extends Schema.Schema<any, string, any>>(schema: S, key: string): Key<Schema.Schema.Type<S>> => {
63
+ return {
64
+ key,
65
+ get: get(schema, key),
66
+ set: (value: Schema.Schema.Type<S>) => set(schema, key, value),
67
+ delete: () => deleteKey(key),
68
+ pipe(...args: any) {
69
+ return Pipeable.pipeArguments(this, arguments);
70
+ },
71
+ };
72
+ };
73
+
74
+ /**
75
+ * Typed key in a storage service with a default value.
76
+ */
77
+ export interface KeyWithDefault<T, U> extends Pipeable.Pipeable {
78
+ readonly key: string;
79
+ get: Effect.Effect<T | U, never, StorageService>;
80
+ set(value: U): Effect.Effect<void, never, StorageService>;
81
+ delete(): Effect.Effect<void, never, StorageService>;
82
+ }
83
+
84
+ /**
85
+ * Assign a default value to a key if it is not present.
86
+ */
87
+ export const withDefault =
88
+ <T>(getDefault: () => NoInfer<T>) =>
89
+ (key: Key<T>): KeyWithDefault<T, T> => {
90
+ return {
91
+ key: key.key,
92
+ get: key.get.pipe(Effect.map(Option.getOrElse(() => getDefault()))),
93
+ set: (value) => key.set(value),
94
+ delete: () => key.delete(),
95
+ pipe(...args: any) {
96
+ return Pipeable.pipeArguments(this, arguments);
97
+ },
98
+ };
99
+ };
@@ -11,40 +11,46 @@ import * as SchemaAST from 'effect/SchemaAST';
11
11
  import { AiModelResolver, AiService } from '@dxos/ai';
12
12
  import { AnthropicResolver } from '@dxos/ai/resolvers';
13
13
  import { LifecycleState, Resource } from '@dxos/context';
14
- import { Database, Ref, Type } from '@dxos/echo';
14
+ import { Database, Feed, JsonSchema, Ref, type Type } from '@dxos/echo';
15
15
  import { refFromEncodedReference } from '@dxos/echo/internal';
16
- import { EchoClient, type EchoDatabaseImpl, type QueueFactory } from '@dxos/echo-db';
16
+ import { EchoClient, type EchoDatabaseImpl, type QueueFactory, createFeedServiceLayer } from '@dxos/echo-db';
17
17
  import { runAndForwardErrors } from '@dxos/effect';
18
18
  import { assertState, failedInvariant, invariant } from '@dxos/invariant';
19
19
  import { PublicKey } from '@dxos/keys';
20
20
  import { type FunctionProtocol } from '@dxos/protocols';
21
21
 
22
22
  import { FunctionError } from '../errors';
23
- import { FunctionDefinition, type FunctionServices } from '../sdk';
23
+ import { type FunctionServices } from '../sdk';
24
24
  import { CredentialsService, FunctionInvocationService, QueueService, TracingService } from '../services';
25
+ import * as Trace from '../Trace';
26
+ import { Operation } from '@dxos/operation';
25
27
 
26
28
  import { FunctionsAiHttpClient } from './functions-ai-http-client';
27
29
 
28
30
  /**
29
31
  * Wraps a function handler made with `defineFunction` to a protocol that the functions-runtime expects.
30
32
  */
31
- export const wrapFunctionHandler = (func: FunctionDefinition): FunctionProtocol.Func => {
32
- if (!FunctionDefinition.isFunction(func)) {
33
- throw new TypeError('Invalid function definition');
33
+ export const wrapFunctionHandler = (func: Operation.WithHandler<Operation.Definition.Any>): FunctionProtocol.Func => {
34
+ if (!Operation.isOperationWithHandler(func)) {
35
+ throw new TypeError('Expected operation with handler');
34
36
  }
35
37
 
38
+ const serviceTags = func.services.map((service) => service.key);
39
+
36
40
  return {
37
41
  meta: {
38
- key: func.key,
39
- name: func.name,
40
- description: func.description,
41
- inputSchema: Type.toJsonSchema(func.inputSchema),
42
- outputSchema: func.outputSchema === undefined ? undefined : Type.toJsonSchema(func.outputSchema),
43
- services: func.services,
42
+ key: func.meta.key,
43
+ name: func.meta.name,
44
+ description: func.meta.description,
45
+ inputSchema: JsonSchema.toJsonSchema(func.input),
46
+ outputSchema: func.output === undefined ? undefined : JsonSchema.toJsonSchema(func.output),
47
+ services: func.services.map((service) => service.key),
44
48
  },
45
49
  handler: async ({ data, context }) => {
46
50
  if (
47
- (func.services.includes(Database.Service.key) || func.services.includes(QueueService.key)) &&
51
+ (serviceTags.includes(Database.Service.key) ||
52
+ serviceTags.includes(QueueService.key) ||
53
+ serviceTags.includes(Feed.Service.key)) &&
48
54
  (!context.services.dataService || !context.services.queryService)
49
55
  ) {
50
56
  throw new FunctionError({
@@ -54,9 +60,9 @@ export const wrapFunctionHandler = (func: FunctionDefinition): FunctionProtocol.
54
60
 
55
61
  // eslint-disable-next-line no-useless-catch
56
62
  try {
57
- if (!SchemaAST.isAnyKeyword(func.inputSchema.ast)) {
63
+ if (!SchemaAST.isAnyKeyword(func.input.ast)) {
58
64
  try {
59
- Schema.validateSync(func.inputSchema)(data);
65
+ Schema.validateSync(func.input)(data);
60
66
  } catch (error) {
61
67
  throw new FunctionError({ message: 'Invalid input schema', cause: error });
62
68
  }
@@ -66,19 +72,15 @@ export const wrapFunctionHandler = (func: FunctionDefinition): FunctionProtocol.
66
72
 
67
73
  if (func.types.length > 0) {
68
74
  invariant(funcContext.db, 'Database is required for functions with types');
69
- await funcContext.db.graph.schemaRegistry.register(func.types as Type.Entity.Any[]);
75
+ await funcContext.db.graph.schemaRegistry.register(func.types as Type.AnyEntity[]);
70
76
  }
71
77
 
72
78
  const dataWithDecodedRefs =
73
- funcContext.db && !SchemaAST.isAnyKeyword(func.inputSchema.ast)
74
- ? decodeRefsFromSchema(func.inputSchema.ast, data, funcContext.db)
79
+ funcContext.db && !SchemaAST.isAnyKeyword(func.input.ast)
80
+ ? decodeRefsFromSchema(func.input.ast, data, funcContext.db)
75
81
  : data;
76
82
 
77
- let result = await func.handler({
78
- // TODO(dmaretskyi): Fix the types.
79
- context: context as any,
80
- data: dataWithDecodedRefs,
81
- });
83
+ let result: any = await func.handler(dataWithDecodedRefs);
82
84
 
83
85
  if (Effect.isEffect(result)) {
84
86
  result = await runAndForwardErrors(
@@ -89,8 +91,8 @@ export const wrapFunctionHandler = (func: FunctionDefinition): FunctionProtocol.
89
91
  );
90
92
  }
91
93
 
92
- if (func.outputSchema && !SchemaAST.isAnyKeyword(func.outputSchema.ast)) {
93
- Schema.validateSync(func.outputSchema)(result);
94
+ if (func.output && !SchemaAST.isAnyKeyword(func.output.ast)) {
95
+ Schema.validateSync(func.output)(result);
94
96
  }
95
97
 
96
98
  return result;
@@ -149,8 +151,9 @@ class FunctionContext extends Resource {
149
151
  createLayer(): Layer.Layer<FunctionServices> {
150
152
  assertState(this._lifecycleState === LifecycleState.OPEN, 'FunctionContext is not open');
151
153
 
152
- const dbLayer = this.db ? Database.Service.layer(this.db) : Database.Service.notAvailable;
154
+ const dbLayer = this.db ? Database.layer(this.db) : Database.notAvailable;
153
155
  const queuesLayer = this.queues ? QueueService.layer(this.queues) : QueueService.notAvailable;
156
+ const feedLayer = this.queues ? createFeedServiceLayer(this.queues) : Feed.notAvailable;
154
157
  const credentials = dbLayer
155
158
  ? CredentialsService.layerFromDatabase({ caching: true }).pipe(Layer.provide(dbLayer))
156
159
  : CredentialsService.configuredLayer([]);
@@ -173,12 +176,15 @@ class FunctionContext extends Resource {
173
176
  : AiService.notAvailable;
174
177
 
175
178
  return Layer.mergeAll(
176
- dbLayer, //
179
+ dbLayer,
177
180
  queuesLayer,
181
+ feedLayer,
178
182
  credentials,
179
183
  functionInvocationService,
180
184
  aiLayer,
181
185
  tracing,
186
+ // TODO(dmaretskyi): Forward trace events.
187
+ Trace.writerLayerNoop,
182
188
  );
183
189
  }
184
190
  }
package/src/sdk.ts CHANGED
@@ -2,15 +2,9 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import type * as Context from 'effect/Context';
6
- import * as Effect from 'effect/Effect';
7
- import * as Schema from 'effect/Schema';
8
-
9
5
  import { type AiService } from '@dxos/ai';
10
- import { Obj, Type } from '@dxos/echo';
11
- import { type Database } from '@dxos/echo';
12
- import { assertArgument, failedInvariant } from '@dxos/invariant';
13
- import { Operation } from '@dxos/operation';
6
+ import { type Database, type Feed } from '@dxos/echo';
7
+ import { type Trace } from '@dxos/functions';
14
8
 
15
9
  import {
16
10
  type CredentialsService,
@@ -18,8 +12,6 @@ import {
18
12
  type QueueService,
19
13
  type TracingService,
20
14
  } from './services';
21
- import { Function } from './types';
22
- import { getUserFunctionIdInMetadata, setUserFunctionIdInMetadata } from './types';
23
15
 
24
16
  // TODO(burdon): Model after http request. Ref Lambda/OpenFaaS.
25
17
  // https://docs.aws.amazon.com/lambda/latest/dg/typescript-handler.html
@@ -33,257 +25,15 @@ export type InvocationServices = TracingService;
33
25
 
34
26
  /**
35
27
  * Services that are available to invoked functions.
28
+ * @deprecated
36
29
  */
37
30
  export type FunctionServices =
38
31
  | InvocationServices
39
32
  | AiService.AiService
40
33
  | CredentialsService
41
34
  | Database.Service
35
+ // TODO(wittjosiah): Remove QueueService — use Feed.Service instead.
42
36
  | QueueService
37
+ | Feed.Service
38
+ | Trace.TraceService
43
39
  | FunctionInvocationService;
44
-
45
- /**
46
- * Function handler.
47
- */
48
- export type FunctionHandler<TData = {}, TOutput = any, S extends FunctionServices = FunctionServices> = (params: {
49
- /**
50
- * Context available to the function.
51
- */
52
- context: FunctionContext;
53
-
54
- /**
55
- * Data passed as the input to the function.
56
- * Must match the function's input schema.
57
- * This will be the payload from the trigger or other data passed into the function in a workflow.
58
- */
59
- data: TData;
60
- }) => TOutput | Promise<TOutput> | Effect.Effect<TOutput, any, S>;
61
-
62
- /**
63
- * Function context.
64
- */
65
- export interface FunctionContext {
66
- // TODO(dmaretskyi): Consider what we should put into context.
67
- }
68
-
69
- const typeId = Symbol.for('@dxos/functions/FunctionDefinition');
70
-
71
- export type FunctionDefinition<T = any, O = any, S extends FunctionServices = FunctionServices> = {
72
- [typeId]: true;
73
- key: string;
74
- name: string;
75
- description?: string;
76
- inputSchema: Schema.Schema<T, any>;
77
- outputSchema?: Schema.Schema<O, any>;
78
-
79
- /**
80
- * List of types the function uses.
81
- * This is used to ensure that the types are available when the function is executed.
82
- */
83
- types: readonly Type.Entity.Any[];
84
-
85
- /**
86
- * Keys of the required services.
87
- */
88
- services: readonly string[];
89
-
90
- handler: FunctionHandler<T, O, S>;
91
- meta?: {
92
- /**
93
- * Tools that are projected from functions have this annotation.
94
- *
95
- * deployedFunctionId:
96
- * - Backend deployment ID assigned by the EDGE function service (typically a UUID).
97
- * - Used for remote invocation via `FunctionInvocationService` → `RemoteFunctionExecutionService`.
98
- * - Persisted on the corresponding ECHO `Function.Function` object's metadata under the
99
- * `FUNCTIONS_META_KEY` and retrieved with `getUserFunctionIdInMetadata`.
100
- */
101
- deployedFunctionId?: string;
102
- };
103
- };
104
-
105
- export declare namespace FunctionDefinition {
106
- export type Any = FunctionDefinition<any, any, any>;
107
- export type Input<T extends Any> = T extends FunctionDefinition<infer I, infer _O, infer _S> ? I : never;
108
- export type Output<T extends Any> = T extends FunctionDefinition<infer _I, infer O, infer _S> ? O : never;
109
- export type Services<T extends Any> = T extends FunctionDefinition<infer _I, infer _O, infer S> ? S : never;
110
- }
111
-
112
- export type FunctionProps<T, O> = {
113
- key: string;
114
- name: string;
115
- description?: string;
116
- inputSchema: Schema.Schema<T, any>;
117
- outputSchema?: Schema.Schema<O, any>;
118
-
119
- /**
120
- * List of types the function uses.
121
- * This is used to ensure that the types are available when the function is executed.
122
- */
123
- types?: readonly Type.Entity.Any[];
124
- // TODO(dmaretskyi): This currently doesn't cause a compile-time error if the handler requests a service that is not specified
125
- services?: readonly Context.Tag<any, any>[];
126
-
127
- handler: FunctionHandler<T, O, FunctionServices>;
128
- };
129
-
130
- // TODO(dmaretskyi): Output type doesn't get typechecked.
131
- export const defineFunction: {
132
- <I, O>(params: FunctionProps<I, O>): FunctionDefinition<I, O, FunctionServices>;
133
- } = ({ key, name, description, inputSchema, outputSchema = Schema.Any, handler, types, services }) => {
134
- if (!Schema.isSchema(inputSchema)) {
135
- throw new Error('Input schema must be a valid schema');
136
- }
137
- if (typeof handler !== 'function') {
138
- throw new Error('Handler must be a function');
139
- }
140
-
141
- // Captures the function definition location.
142
- const limit = Error.stackTraceLimit;
143
- Error.stackTraceLimit = 2;
144
- const traceError = new Error();
145
- Error.stackTraceLimit = limit;
146
- let cache: false | string = false;
147
- const captureStackTrace = () => {
148
- if (cache !== false) {
149
- return cache;
150
- }
151
- if (traceError.stack !== undefined) {
152
- const stack = traceError.stack.split('\n');
153
- if (stack[2] !== undefined) {
154
- cache = stack[2].trim();
155
- return cache;
156
- }
157
- }
158
- };
159
-
160
- const handlerWithSpan = (...args: any[]) => {
161
- const result = (handler as any)(...args);
162
- if (Effect.isEffect(result)) {
163
- return Effect.withSpan(result, `${key ?? name}`, {
164
- captureStackTrace,
165
- });
166
- }
167
- return result;
168
- };
169
-
170
- return {
171
- [typeId]: true,
172
- key,
173
- name,
174
- description,
175
- inputSchema,
176
- outputSchema,
177
- handler: handlerWithSpan,
178
- types: types ?? [],
179
- services: !services ? [] : getServiceKeys(services),
180
- } satisfies FunctionDefinition.Any;
181
- };
182
-
183
- const getServiceKeys = (services: readonly Context.Tag<any, any>[]) => {
184
- return services.map((tag: any) => {
185
- if (typeof tag.key === 'string') {
186
- return tag.key;
187
- }
188
- failedInvariant();
189
- });
190
- };
191
-
192
- /**
193
- * Converts a FunctionDefinition to an OperationDefinition with handler.
194
- * The function handler is adapted to the OperationHandler format.
195
- *
196
- * Note: FunctionDefinition stores service keys as strings, not Tag types,
197
- * so we can't use Operation.withHandler's type inference here.
198
- */
199
- export const toOperation = <T, O, S extends FunctionServices = FunctionServices>(
200
- functionDef: FunctionDefinition<T, O, S>,
201
- ): Operation.Definition<T, O> & { handler: Operation.Handler<T, O, any, S> } => {
202
- const op = Operation.make({
203
- schema: {
204
- input: functionDef.inputSchema,
205
- output: functionDef.outputSchema ?? Schema.Any,
206
- },
207
- meta: {
208
- key: functionDef.key,
209
- name: functionDef.name,
210
- description: functionDef.description,
211
- },
212
- });
213
-
214
- // Adapt FunctionHandler signature to OperationHandler format.
215
- // FunctionHandler expects { context, data }, OperationHandler expects just input.
216
- const operationHandler: Operation.Handler<T, O, any, S> = (input: T) => {
217
- const result = functionDef.handler({
218
- context: {} as FunctionContext,
219
- data: input,
220
- });
221
-
222
- // Convert Promise or plain value to Effect.
223
- if (Effect.isEffect(result)) {
224
- return result;
225
- }
226
- if (result instanceof Promise) {
227
- return Effect.tryPromise(() => result);
228
- }
229
- return Effect.succeed(result as O);
230
- };
231
-
232
- // Manually attach handler since FunctionDefinition stores service keys as strings,
233
- // not Tag types, so withHandler's type inference doesn't apply.
234
- return {
235
- ...op,
236
- handler: operationHandler,
237
- };
238
- };
239
-
240
- export const FunctionDefinition = {
241
- make: defineFunction,
242
- isFunction: (value: unknown): value is FunctionDefinition.Any => {
243
- return typeof value === 'object' && value !== null && Symbol.for('@dxos/functions/FunctionDefinition') in value;
244
- },
245
- serialize: (functionDef: FunctionDefinition.Any): Function.Function => {
246
- assertArgument(FunctionDefinition.isFunction(functionDef), 'functionDef');
247
- return serializeFunction(functionDef);
248
- },
249
- deserialize: (functionObj: Function.Function): FunctionDefinition.Any => {
250
- assertArgument(Obj.instanceOf(Function.Function, functionObj), 'functionObj');
251
- return deserializeFunction(functionObj);
252
- },
253
- toOperation,
254
- };
255
-
256
- export const serializeFunction = (functionDef: FunctionDefinition.Any): Function.Function => {
257
- const fn = Function.make({
258
- key: functionDef.key,
259
- name: functionDef.name,
260
- version: '0.1.0',
261
- description: functionDef.description,
262
- inputSchema: Type.toJsonSchema(functionDef.inputSchema),
263
- outputSchema: !functionDef.outputSchema ? undefined : Type.toJsonSchema(functionDef.outputSchema),
264
- services: functionDef.services,
265
- });
266
- if (functionDef.meta?.deployedFunctionId) {
267
- Obj.change(fn, (fn) => setUserFunctionIdInMetadata(Obj.getMeta(fn), functionDef.meta!.deployedFunctionId!));
268
- }
269
- return fn;
270
- };
271
-
272
- export const deserializeFunction = (functionObj: Function.Function): FunctionDefinition<unknown, unknown> => {
273
- return {
274
- [typeId]: true,
275
- // TODO(dmaretskyi): Fix key.
276
- key: functionObj.key ?? functionObj.name,
277
- name: functionObj.name,
278
- description: functionObj.description,
279
- inputSchema: !functionObj.inputSchema ? Schema.Unknown : Type.toEffectSchema(functionObj.inputSchema),
280
- outputSchema: !functionObj.outputSchema ? undefined : Type.toEffectSchema(functionObj.outputSchema),
281
- // TODO(dmaretskyi): This should throw error.
282
- handler: () => {},
283
- services: functionObj.services ?? [],
284
- types: [],
285
- meta: {
286
- deployedFunctionId: getUserFunctionIdInMetadata(Obj.getMeta(functionObj)),
287
- },
288
- };
289
- };
@@ -52,7 +52,7 @@ export type ComputeEventPayload = Schema.Schema.Type<typeof ComputeEventPayload>
52
52
 
53
53
  export const ComputeEvent = Schema.Struct({
54
54
  payload: ComputeEventPayload,
55
- }).pipe(Type.object({ typename: 'dxos.org/type/ComputeEvent', version: '0.1.0' }));
55
+ }).pipe(Type.object({ typename: 'org.dxos.type.computeEvent', version: '0.1.0' }));
56
56
 
57
57
  /**
58
58
  * Logs event for the compute workflows.
@@ -6,17 +6,18 @@ import * as Effect from 'effect/Effect';
6
6
  import * as Layer from 'effect/Layer';
7
7
 
8
8
  import type { FunctionNotFoundError } from '../errors';
9
- import { type FunctionDefinition, type InvocationServices } from '../sdk';
9
+ import { type InvocationServices } from '../sdk';
10
+ import { Operation } from '@dxos/operation';
10
11
 
11
12
  export class FunctionInvocationService extends Context.Tag('@dxos/functions/FunctionInvocationService')<
12
13
  FunctionInvocationService,
13
14
  {
14
15
  invokeFunction<I, O>(
15
- functionDef: FunctionDefinition<I, O, any>,
16
+ functionDef: Operation.Definition<I, O, any>,
16
17
  input: I,
17
18
  ): Effect.Effect<O, never, InvocationServices>;
18
19
 
19
- resolveFunction(key: string): Effect.Effect<FunctionDefinition.Any, FunctionNotFoundError>;
20
+ resolveFunction(key: string): Effect.Effect<Operation.Definition.Any, FunctionNotFoundError>;
20
21
  }
21
22
  >() {
22
23
  static layerNotAvailable = Layer.succeed(FunctionInvocationService, {
@@ -25,13 +26,13 @@ export class FunctionInvocationService extends Context.Tag('@dxos/functions/Func
25
26
  });
26
27
 
27
28
  static invokeFunction = <I, O>(
28
- functionDef: FunctionDefinition<I, O, any>,
29
+ functionDef: Operation.Definition<I, O, any>,
29
30
  input: I,
30
31
  ): Effect.Effect<O, never, FunctionInvocationService | InvocationServices> =>
31
32
  Effect.serviceFunctionEffect(FunctionInvocationService, (service) => service.invokeFunction)(functionDef, input);
32
33
 
33
34
  static resolveFunction = (
34
35
  key: string,
35
- ): Effect.Effect<FunctionDefinition.Any, FunctionNotFoundError, FunctionInvocationService> =>
36
+ ): Effect.Effect<Operation.Definition.Any, FunctionNotFoundError, FunctionInvocationService> =>
36
37
  Effect.serviceFunctionEffect(FunctionInvocationService, (service) => service.resolveFunction)(key);
37
38
  }
@@ -6,12 +6,13 @@ import * as Context from 'effect/Context';
6
6
  import * as Effect from 'effect/Effect';
7
7
  import * as Layer from 'effect/Layer';
8
8
 
9
- import type { Entity } from '@dxos/echo';
10
- import type { Queue, QueueAPI, QueueFactory } from '@dxos/echo-db';
9
+ import { type Entity } from '@dxos/echo';
10
+ import { createFeedServiceLayer, type Queue, type QueueAPI, type QueueFactory } from '@dxos/echo-db';
11
11
  import type { DXN, QueueSubspaceTag } from '@dxos/keys';
12
12
 
13
13
  /**
14
14
  * Gives access to all queues.
15
+ * @deprecated Use FeedService instead.
15
16
  */
16
17
  export class QueueService extends Context.Tag('@dxos/functions/QueueService')<
17
18
  QueueService,
@@ -80,3 +81,10 @@ export class ContextQueueService extends Context.Tag('@dxos/functions/ContextQue
80
81
  >() {
81
82
  static layer = (queue: Queue) => Layer.succeed(ContextQueueService, { queue });
82
83
  }
84
+
85
+ export const feedServiceFromQueueServiceLayer = Layer.unwrapEffect(
86
+ Effect.gen(function* () {
87
+ const { queues } = yield* QueueService;
88
+ return createFeedServiceLayer(queues);
89
+ }),
90
+ );
@@ -7,7 +7,7 @@ import * as Effect from 'effect/Effect';
7
7
  import * as Layer from 'effect/Layer';
8
8
 
9
9
  import { AgentStatus } from '@dxos/ai';
10
- import { type DXN, Obj } from '@dxos/echo';
10
+ import { type DXN, Obj, Ref } from '@dxos/echo';
11
11
  import { ObjectId } from '@dxos/keys';
12
12
  import { Message } from '@dxos/types';
13
13
 
@@ -16,6 +16,7 @@ import type { Trigger } from '../types';
16
16
  /**
17
17
  * Provides a way for compute primitives (functions, workflows, tools)
18
18
  * to emit an execution trace as a series of structured ECHO objects.
19
+ * @deprecated Use Trace.TraceService instead.
19
20
  */
20
21
  export class TracingService extends Context.Tag('@dxos/functions/TracingService')<
21
22
  TracingService,
@@ -31,6 +32,11 @@ export class TracingService extends Context.Tag('@dxos/functions/TracingService'
31
32
  */
32
33
  write: (event: Obj.Unknown, traceContext: TracingService.TraceContext) => void;
33
34
 
35
+ /**
36
+ * Write an ephemeral event.
37
+ */
38
+ ephemeral: (event: Obj.Unknown, traceContext: TracingService.TraceContext) => void;
39
+
34
40
  traceInvocationStart({
35
41
  payload,
36
42
  target,
@@ -51,6 +57,7 @@ export class TracingService extends Context.Tag('@dxos/functions/TracingService'
51
57
  static noop: Context.Tag.Service<TracingService> = {
52
58
  getTraceContext: () => ({}),
53
59
  write: () => {},
60
+ ephemeral: () => {},
54
61
  traceInvocationStart: () =>
55
62
  Effect.sync(() => ({ invocationId: ObjectId.random(), invocationTraceQueue: undefined })),
56
63
  traceInvocationEnd: () => Effect.sync(() => {}),
@@ -69,6 +76,7 @@ export class TracingService extends Context.Tag('@dxos/functions/TracingService'
69
76
  const context = mapContext(tracing.getTraceContext());
70
77
  return {
71
78
  write: (event, context) => tracing.write(event, context),
79
+ ephemeral: (event, context) => tracing.ephemeral(event, context),
72
80
  getTraceContext: () => context,
73
81
  traceInvocationStart: () => Effect.die('Tracing invocation inside another invocation is not supported.'),
74
82
  traceInvocationEnd: () => Effect.die('Tracing invocation inside another invocation is not supported.'),
@@ -106,7 +114,7 @@ export class TracingService extends Context.Tag('@dxos/functions/TracingService'
106
114
  });
107
115
 
108
116
  static emitConverationMessage: (
109
- data: Obj.MakeProps<typeof Message.Message>,
117
+ data: Obj.MakeProps<typeof Message.Message> | Message.Message,
110
118
  ) => Effect.Effect<void, never, TracingService> = Effect.fnUntraced(function* (data) {
111
119
  const tracing = yield* TracingService;
112
120
  tracing.write(
@@ -121,6 +129,12 @@ export class TracingService extends Context.Tag('@dxos/functions/TracingService'
121
129
  tracing.getTraceContext(),
122
130
  );
123
131
  });
132
+
133
+ static emitEphemeralMessage: (data: Message.Message) => Effect.Effect<void, never, TracingService> =
134
+ Effect.fnUntraced(function* (data) {
135
+ const tracing = yield* TracingService;
136
+ tracing.ephemeral(data, tracing.getTraceContext());
137
+ });
124
138
  }
125
139
 
126
140
  export namespace TracingService {
@@ -158,6 +172,25 @@ export namespace TracingService {
158
172
  id: string;
159
173
  kind: Trigger.Kind;
160
174
  };
175
+ chat?: Ref.Ref<Obj.Unknown>;
176
+ process?: {
177
+ pid: string;
178
+ parentPid?: string;
179
+ /**
180
+ * Key of the executable.
181
+ */
182
+ key: string;
183
+
184
+ /**
185
+ * Process name.
186
+ */
187
+ name?: string;
188
+
189
+ /**
190
+ * Target object that the process is assigned to.
191
+ */
192
+ target?: string;
193
+ };
161
194
  }
162
195
  }
163
196