@adobe/data 0.9.28 → 0.9.30

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.
@@ -0,0 +1,5 @@
1
+ import type { AgenticService } from "./agentic-service.js";
2
+ /** Map of link key → agentic service. In a separate file so declaration emit does not use a private name (TS4033/TS4049). */
3
+ export type AgenticServiceLinks = {
4
+ [key: string]: AgenticService;
5
+ };
@@ -0,0 +1,3 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+ export {};
3
+ //# sourceMappingURL=agentic-service-links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentic-service-links.js","sourceRoot":"","sources":["../../../src/service/agentic-service/agentic-service-links.ts"],"names":[],"mappings":"AAAA,uDAAuD"}
@@ -2,15 +2,16 @@ import { Observe } from "../../observe/index.js";
2
2
  import type { Service } from "../index.js";
3
3
  import type { Action } from "./action.js";
4
4
  import type { ActionError } from "./action-error.js";
5
+ import type { AgenticServiceLinks } from "./agentic-service-links.js";
5
6
  import type { State } from "./state.js";
6
7
  /**
7
8
  * An agentic service provides a set of valid states and actions
8
9
  * which are conditionally available based upon the current state.
9
- * This is designed to serve as a foundation for agentic systems.
10
- * An MCP or other agentic protocol could be built upon this service interface.
10
+ * Optional links expose other agentic services (e.g. parent, children)
11
+ * for navigation or delegation. Designed as a foundation for agentic
12
+ * systems; MCP or other agentic protocols can be built on this interface.
11
13
  */
12
14
  export interface AgenticService extends Service {
13
- description: Observe<string>;
14
15
  states: Observe<{
15
16
  [key: string]: State;
16
17
  }>;
@@ -18,5 +19,7 @@ export interface AgenticService extends Service {
18
19
  [key: string]: Action;
19
20
  }>;
20
21
  execute: (action: string, input: unknown) => Promise<void | ActionError>;
22
+ /** When set, observable map of links to other agentic services (key → AgenticService). */
23
+ links?: Observe<AgenticServiceLinks>;
21
24
  }
22
25
  export * as AgenticService from "./public.js";
@@ -1 +1 @@
1
- {"version":3,"file":"agentic-service.js","sourceRoot":"","sources":["../../../src/service/agentic-service/agentic-service.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAuBvD,OAAO,KAAK,cAAc,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"agentic-service.js","sourceRoot":"","sources":["../../../src/service/agentic-service/agentic-service.ts"],"names":[],"mappings":"AAAA,uDAAuD;AA2BvD,OAAO,KAAK,cAAc,MAAM,aAAa,CAAC"}
@@ -1,21 +1,24 @@
1
1
  import { Observe } from "../../observe/index.js";
2
2
  import { Schema } from "../../schema/index.js";
3
3
  import { AgenticService } from "./agentic-service.js";
4
- /** State declaration: has top-level `type` (JSON Schema) + `description` */
5
- type StateDeclaration<S extends Schema & {
6
- type: string;
7
- } = Schema & {
8
- type: string;
9
- }> = S & {
4
+ /** State declaration: kind + schema for value + description */
5
+ type StateDeclaration<S extends Schema = Schema> = {
6
+ type: "state";
7
+ schema: S;
10
8
  description: string;
11
9
  };
12
- /** Action declaration: `description`, optional `input` schema, no `type` */
10
+ /** Action declaration: kind + description + optional input schema */
13
11
  type ActionDeclaration<I extends Schema | false | undefined = undefined> = {
12
+ type: "action";
14
13
  description: string;
15
14
  input?: I;
16
- type?: never;
17
15
  };
18
- type DeclarationEntry = StateDeclaration | ActionDeclaration<Schema | false | undefined>;
16
+ /** Link declaration: kind + optional description */
17
+ type LinkDeclaration = {
18
+ type: "link";
19
+ description?: string;
20
+ };
21
+ type DeclarationEntry = StateDeclaration | ActionDeclaration<Schema | false | undefined> | LinkDeclaration;
19
22
  type Declarations = Record<string, DeclarationEntry>;
20
23
  type ConditionalFromDeclarations<D extends Declarations> = Partial<{
21
24
  [K in keyof D]: Observe<boolean>;
@@ -23,9 +26,11 @@ type ConditionalFromDeclarations<D extends Declarations> = Partial<{
23
26
  /**
24
27
  * Creates an AgenticService from a config with interface, implementation, and optional conditional.
25
28
  *
26
- * Interface: flat map of states (have `type`) and actions (have `description`, optional `input`).
27
- * Implementation: same keys, states Observe<...>, actions (input?) => ...
28
- * Conditional: optional per-key Observe<boolean> for enablement.
29
+ * Interface: flat map of declarations. Each entry has type "state" | "action" | "link":
30
+ * - state: { type: "state", schema, description }implementation: Observe<value>
31
+ * - action: { type: "action", description, input? } → implementation: (input?) => ...
32
+ * - link: { type: "link", description? } → implementation: AgenticService | Observe<AgenticService>
33
+ * Conditional: optional per-key Observe<boolean> for enablement (applies to states, actions, and links).
29
34
  */
30
35
  export declare function create<const D extends Declarations>(config: {
31
36
  description: string;
@@ -33,13 +38,17 @@ export declare function create<const D extends Declarations>(config: {
33
38
  implementation: ImplementationFromDeclarations<D>;
34
39
  conditional?: ConditionalFromDeclarations<D>;
35
40
  }): AgenticService;
36
- /** Implementation map derived from interface: states → Observe, actions → execute fn */
41
+ /** Implementation map derived from interface: state → Observe, action → execute fn, link → AgenticService | Observe<AgenticService> */
37
42
  export type ImplementationFromDeclarations<D extends Declarations> = {
38
43
  [K in keyof D]: D[K] extends {
39
- type: string;
40
- } ? Observe<Schema.ToType<D[K]>> : D[K] extends {
41
- input: infer I extends Schema | false;
42
- } ? ExecuteFromSchema<I> : ExecuteFromSchema<false>;
44
+ type: "state";
45
+ schema: infer S extends Schema;
46
+ } ? Observe<Schema.ToType<S>> : D[K] extends {
47
+ type: "action";
48
+ input?: infer I extends Schema | false | undefined;
49
+ } ? ExecuteFromSchema<I extends undefined ? false : I> : D[K] extends {
50
+ type: "link";
51
+ } ? AgenticService | Observe<AgenticService> : never;
43
52
  };
44
53
  type ExecuteFromSchema<S extends Schema | false> = S extends false ? (() => Promise<void | AgenticService.ActionError> | void) : ((input: Schema.ToType<S>) => Promise<void | AgenticService.ActionError> | void);
45
54
  export {};
@@ -1,42 +1,41 @@
1
1
  // © 2026 Adobe. MIT License. See /LICENSE for details.
2
2
  import { Observe } from "../../observe/index.js";
3
- function isStateDeclaration(entry) {
4
- return "type" in entry && typeof entry.type === "string";
5
- }
6
3
  function getActionSchema(entry) {
7
- if (isStateDeclaration(entry))
8
- return false;
9
- const actionEntry = entry;
10
- return actionEntry.input ?? false;
4
+ return entry.input ?? false;
11
5
  }
12
6
  /**
13
7
  * Creates an AgenticService from a config with interface, implementation, and optional conditional.
14
8
  *
15
- * Interface: flat map of states (have `type`) and actions (have `description`, optional `input`).
16
- * Implementation: same keys, states Observe<...>, actions (input?) => ...
17
- * Conditional: optional per-key Observe<boolean> for enablement.
9
+ * Interface: flat map of declarations. Each entry has type "state" | "action" | "link":
10
+ * - state: { type: "state", schema, description }implementation: Observe<value>
11
+ * - action: { type: "action", description, input? } → implementation: (input?) => ...
12
+ * - link: { type: "link", description? } → implementation: AgenticService | Observe<AgenticService>
13
+ * Conditional: optional per-key Observe<boolean> for enablement (applies to states, actions, and links).
18
14
  */
19
15
  export function create(config) {
20
16
  const { interface: iface, implementation, conditional } = config;
21
17
  const alwaysEnabled = Observe.fromConstant(true);
22
18
  const stateKeys = [];
23
19
  const actionKeys = [];
20
+ const linkKeys = [];
24
21
  const stateSchemas = {};
25
22
  const actionMeta = {};
26
23
  for (const [key, entry] of Object.entries(iface)) {
27
- if (isStateDeclaration(entry)) {
24
+ if (entry.type === "state") {
28
25
  stateKeys.push(key);
29
- stateSchemas[key] = entry;
26
+ stateSchemas[key] = entry.schema;
30
27
  }
31
- else {
28
+ else if (entry.type === "action") {
32
29
  actionKeys.push(key);
33
- const a = entry;
34
30
  actionMeta[key] = {
35
- description: a.description,
31
+ description: entry.description,
36
32
  schema: getActionSchema(entry),
37
33
  execute: implementation[key],
38
34
  };
39
35
  }
36
+ else if (entry.type === "link") {
37
+ linkKeys.push(key);
38
+ }
40
39
  }
41
40
  const perStateObservables = {};
42
41
  for (const key of stateKeys) {
@@ -51,6 +50,14 @@ export function create(config) {
51
50
  for (const key of actionKeys) {
52
51
  enabledObservables[key] = conditional?.[key] ?? alwaysEnabled;
53
52
  }
53
+ const implRecord = implementation;
54
+ const perLinkObservables = {};
55
+ for (const key of linkKeys) {
56
+ const raw = implRecord[key];
57
+ const linkObs = typeof raw === "function" ? raw : Observe.fromConstant(raw);
58
+ const enabledObs = conditional?.[key] ?? alwaysEnabled;
59
+ perLinkObservables[key] = Observe.withMap(Observe.fromProperties({ enabled: enabledObs, value: linkObs }), (x) => x);
60
+ }
54
61
  const states = Observe.withMap(Observe.fromProperties(perStateObservables), (raw) => {
55
62
  const result = {};
56
63
  for (const [key, entry] of Object.entries(raw)) {
@@ -80,6 +87,23 @@ export function create(config) {
80
87
  return `Action "${actionName}" is not available`;
81
88
  return entry.execute(input);
82
89
  };
83
- return { serviceName: "agentic-service", states, actions, execute, description: Observe.fromConstant(config.description) };
90
+ const links = linkKeys.length > 0
91
+ ? Observe.withMap(Observe.fromProperties(perLinkObservables), (raw) => {
92
+ const result = {};
93
+ for (const [key, entry] of Object.entries(raw)) {
94
+ const { enabled, value } = entry;
95
+ if (enabled && value)
96
+ result[key] = value;
97
+ }
98
+ return result;
99
+ })
100
+ : undefined;
101
+ return {
102
+ serviceName: "agentic-service",
103
+ states,
104
+ actions,
105
+ execute,
106
+ ...(links !== undefined && { links }),
107
+ };
84
108
  }
85
109
  //# sourceMappingURL=create.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"create.js","sourceRoot":"","sources":["../../../src/service/agentic-service/create.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../../src/service/agentic-service/create.ts"],"names":[],"mappings":"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"}
@@ -19,7 +19,7 @@ describe("AgenticService.create", () => {
19
19
  create({
20
20
  description: "Test",
21
21
  interface: {
22
- currentHealth: { type: "number", description: "Health" },
22
+ currentHealth: { type: "state", schema: { type: "number" }, description: "Health" },
23
23
  },
24
24
  implementation: {
25
25
  currentHealth: db.observe.resources.health,
@@ -32,7 +32,7 @@ describe("AgenticService.create", () => {
32
32
  create({
33
33
  description: "Test",
34
34
  interface: {
35
- playerName: { type: "string", description: "Name" },
35
+ playerName: { type: "state", schema: { type: "string" }, description: "Name" },
36
36
  },
37
37
  implementation: {
38
38
  playerName: db.observe.resources.name,
@@ -45,8 +45,8 @@ describe("AgenticService.create", () => {
45
45
  create({
46
46
  description: "Test",
47
47
  interface: {
48
- currentHealth: { type: "number", description: "Health" },
49
- playerName: { type: "string", description: "Name" },
48
+ currentHealth: { type: "state", schema: { type: "number" }, description: "Health" },
49
+ playerName: { type: "state", schema: { type: "string" }, description: "Name" },
50
50
  },
51
51
  implementation: {
52
52
  currentHealth: db.observe.resources.health,
@@ -58,7 +58,7 @@ describe("AgenticService.create", () => {
58
58
  create({
59
59
  description: "Test",
60
60
  interface: {
61
- setHealth: { description: "Set health", input: { type: "number" } },
61
+ setHealth: { type: "action", description: "Set health", input: { type: "number" } },
62
62
  },
63
63
  implementation: {
64
64
  setHealth: async (_input) => { },
@@ -69,7 +69,7 @@ describe("AgenticService.create", () => {
69
69
  create({
70
70
  description: "Test",
71
71
  interface: {
72
- reset: { description: "Reset to defaults" },
72
+ reset: { type: "action", description: "Reset to defaults" },
73
73
  },
74
74
  implementation: {
75
75
  reset: async () => { },
@@ -81,14 +81,17 @@ describe("AgenticService.create", () => {
81
81
  description: "Test",
82
82
  interface: {
83
83
  stats: {
84
- type: "object",
85
- description: "Stats",
86
- properties: {
87
- hp: { type: "number" },
88
- label: { type: "string" },
84
+ type: "state",
85
+ schema: {
86
+ type: "object",
87
+ properties: {
88
+ hp: { type: "number" },
89
+ label: { type: "string" },
90
+ },
91
+ required: ["hp"],
92
+ additionalProperties: false,
89
93
  },
90
- required: ["hp"],
91
- additionalProperties: false,
94
+ description: "Stats",
92
95
  },
93
96
  },
94
97
  implementation: {
@@ -101,6 +104,7 @@ describe("AgenticService.create", () => {
101
104
  description: "Test",
102
105
  interface: {
103
106
  configure: {
107
+ type: "action",
104
108
  description: "Configure",
105
109
  input: {
106
110
  type: "object",
@@ -122,8 +126,8 @@ describe("AgenticService.create", () => {
122
126
  create({
123
127
  description: "Test",
124
128
  interface: {
125
- heal: { description: "Heal", input: { type: "number" } },
126
- reset: { description: "Reset" },
129
+ heal: { type: "action", description: "Heal", input: { type: "number" } },
130
+ reset: { type: "action", description: "Reset" },
127
131
  },
128
132
  implementation: {
129
133
  heal: async (_amount) => { },
@@ -135,8 +139,8 @@ describe("AgenticService.create", () => {
135
139
  create({
136
140
  description: "Test",
137
141
  interface: {
138
- sync: { description: "Sync" },
139
- syncWithInput: { description: "Sync with input", input: { type: "number" } },
142
+ sync: { type: "action", description: "Sync" },
143
+ syncWithInput: { type: "action", description: "Sync with input", input: { type: "number" } },
140
144
  },
141
145
  implementation: {
142
146
  sync: () => { },
@@ -149,8 +153,8 @@ describe("AgenticService.create", () => {
149
153
  create({
150
154
  description: "Test",
151
155
  interface: {
152
- health: { type: "number", description: "Health" },
153
- heal: { description: "Heal", input: { type: "number" } },
156
+ health: { type: "state", schema: { type: "number" }, description: "Health" },
157
+ heal: { type: "action", description: "Heal", input: { type: "number" } },
154
158
  },
155
159
  implementation: {
156
160
  health: db.observe.resources.health,
@@ -169,13 +173,107 @@ describe("AgenticService.create", () => {
169
173
  expect(service.serviceName).toBe("agentic-service");
170
174
  });
171
175
  });
176
+ describe("links", () => {
177
+ it("should expose links observable when link is declared in interface", () => {
178
+ const child = create({
179
+ description: "Child",
180
+ interface: {},
181
+ implementation: {},
182
+ });
183
+ const service = create({
184
+ description: "Parent",
185
+ interface: { child: { type: "link", description: "Child service" } },
186
+ implementation: { child },
187
+ });
188
+ expect(service.links).toBeDefined();
189
+ const received = [];
190
+ service.links((l) => received.push({ ...l }));
191
+ expect(received).toHaveLength(1);
192
+ expect(received[0]).toEqual({ child });
193
+ });
194
+ it("should omit links when links config is omitted", () => {
195
+ const service = create({
196
+ description: "Test",
197
+ interface: {},
198
+ implementation: {},
199
+ });
200
+ expect(service.links).toBeUndefined();
201
+ });
202
+ it("should support Observe<AgenticService> per link for dynamic link target", () => {
203
+ const child = create({
204
+ description: "Child",
205
+ interface: {},
206
+ implementation: {},
207
+ });
208
+ const service = create({
209
+ description: "Parent",
210
+ interface: { child: { type: "link" } },
211
+ implementation: { child: Observe.fromConstant(child) },
212
+ });
213
+ expect(service.links).toBeDefined();
214
+ const received = [];
215
+ service.links((l) => received.push({ ...l }));
216
+ expect(received).toHaveLength(1);
217
+ expect(received[0]).toEqual({ child });
218
+ });
219
+ it("should omit link when conditional is false", () => {
220
+ const child = create({
221
+ description: "Child",
222
+ interface: {},
223
+ implementation: {},
224
+ });
225
+ const service = create({
226
+ description: "Parent",
227
+ interface: { child: { type: "link" } },
228
+ implementation: { child },
229
+ conditional: { child: Observe.fromConstant(false) },
230
+ });
231
+ expect(service.links).toBeDefined();
232
+ const received = [];
233
+ service.links((l) => received.push({ ...l }));
234
+ expect(received).toHaveLength(1);
235
+ expect(received[0]).toEqual({});
236
+ });
237
+ it("should update links when conditional changes", () => {
238
+ const child = create({
239
+ description: "Child",
240
+ interface: {},
241
+ implementation: {},
242
+ });
243
+ const [enabledObs, setEnabled] = Observe.createState(true);
244
+ const service = create({
245
+ description: "Parent",
246
+ interface: { child: { type: "link" } },
247
+ implementation: { child },
248
+ conditional: { child: enabledObs },
249
+ });
250
+ const received = [];
251
+ service.links((l) => received.push({ ...l }));
252
+ expect(received).toHaveLength(1);
253
+ expect(received[0]).toEqual({ child });
254
+ setEnabled(false);
255
+ expect(received).toHaveLength(2);
256
+ expect(received[1]).toEqual({});
257
+ setEnabled(true);
258
+ expect(received).toHaveLength(3);
259
+ expect(received[2]).toEqual({ child });
260
+ });
261
+ it("should have links undefined when no link keys in interface", () => {
262
+ const service = create({
263
+ description: "Test",
264
+ interface: {},
265
+ implementation: {},
266
+ });
267
+ expect(service.links).toBeUndefined();
268
+ });
269
+ });
172
270
  describe("conditional defaults", () => {
173
271
  it("should default state enabled to always true when omitted", async () => {
174
272
  const db = createTestDb();
175
273
  const service = create({
176
274
  description: "Test",
177
275
  interface: {
178
- health: { type: "number", description: "Health" },
276
+ health: { type: "state", schema: { type: "number" }, description: "Health" },
179
277
  },
180
278
  implementation: {
181
279
  health: db.observe.resources.health,
@@ -189,7 +287,7 @@ describe("AgenticService.create", () => {
189
287
  const service = create({
190
288
  description: "Test",
191
289
  interface: {
192
- heal: { description: "Heal", input: { type: "number" } },
290
+ heal: { type: "action", description: "Heal", input: { type: "number" } },
193
291
  },
194
292
  implementation: {
195
293
  heal: async () => { },
@@ -205,7 +303,7 @@ describe("AgenticService.create", () => {
205
303
  const service = create({
206
304
  description: "Test",
207
305
  interface: {
208
- currentHealth: { type: "number", description: "Health" },
306
+ currentHealth: { type: "state", schema: { type: "number" }, description: "Health" },
209
307
  },
210
308
  implementation: {
211
309
  currentHealth: db.observe.resources.health,
@@ -213,7 +311,7 @@ describe("AgenticService.create", () => {
213
311
  });
214
312
  const states = await Observe.toPromise(service.states);
215
313
  expect(states).toHaveProperty("currentHealth");
216
- expect(states.currentHealth.schema).toEqual({ type: "number", description: "Health" });
314
+ expect(states.currentHealth.schema).toEqual({ type: "number" });
217
315
  expect(states.currentHealth.value).toBe(100);
218
316
  });
219
317
  it("should omit disabled states", async () => {
@@ -221,8 +319,8 @@ describe("AgenticService.create", () => {
221
319
  const service = create({
222
320
  description: "Test",
223
321
  interface: {
224
- visible: { type: "number", description: "Visible" },
225
- hidden: { type: "string", description: "Hidden" },
322
+ visible: { type: "state", schema: { type: "number" }, description: "Visible" },
323
+ hidden: { type: "state", schema: { type: "string" }, description: "Hidden" },
226
324
  },
227
325
  implementation: {
228
326
  visible: db.observe.resources.health,
@@ -243,7 +341,7 @@ describe("AgenticService.create", () => {
243
341
  const service = create({
244
342
  description: "Test",
245
343
  interface: {
246
- health: { type: "number", description: "Health" },
344
+ health: { type: "state", schema: { type: "number" }, description: "Health" },
247
345
  },
248
346
  implementation: {
249
347
  health: db.observe.resources.health,
@@ -263,7 +361,7 @@ describe("AgenticService.create", () => {
263
361
  const service = create({
264
362
  description: "Test",
265
363
  interface: {
266
- health: { type: "number", description: "Health" },
364
+ health: { type: "state", schema: { type: "number" }, description: "Health" },
267
365
  },
268
366
  implementation: {
269
367
  health: healthObserve,
@@ -281,7 +379,7 @@ describe("AgenticService.create", () => {
281
379
  const service = create({
282
380
  description: "Test",
283
381
  interface: {
284
- heal: { description: "Heal player", input: { type: "number" } },
382
+ heal: { type: "action", description: "Heal player", input: { type: "number" } },
285
383
  },
286
384
  implementation: {
287
385
  heal: async (_amount) => { },
@@ -297,8 +395,8 @@ describe("AgenticService.create", () => {
297
395
  const service = create({
298
396
  description: "Test",
299
397
  interface: {
300
- available: { description: "Available", input: { type: "number" } },
301
- unavailable: { description: "Unavailable" },
398
+ available: { type: "action", description: "Available", input: { type: "number" } },
399
+ unavailable: { type: "action", description: "Unavailable" },
302
400
  },
303
401
  implementation: {
304
402
  available: async () => { },
@@ -318,7 +416,7 @@ describe("AgenticService.create", () => {
318
416
  const service = create({
319
417
  description: "Test",
320
418
  interface: {
321
- heal: { description: "Heal", input: { type: "number" } },
419
+ heal: { type: "action", description: "Heal", input: { type: "number" } },
322
420
  },
323
421
  implementation: {
324
422
  heal: async () => { },
@@ -338,7 +436,7 @@ describe("AgenticService.create", () => {
338
436
  const service = create({
339
437
  description: "Test",
340
438
  interface: {
341
- setHealth: { description: "Set health", input: { type: "number" } },
439
+ setHealth: { type: "action", description: "Set health", input: { type: "number" } },
342
440
  },
343
441
  implementation: {
344
442
  setHealth: async (input) => { received = input; },
@@ -353,7 +451,7 @@ describe("AgenticService.create", () => {
353
451
  const service = create({
354
452
  description: "Test",
355
453
  interface: {
356
- reset: { description: "Reset" },
454
+ reset: { type: "action", description: "Reset" },
357
455
  },
358
456
  implementation: {
359
457
  reset: async () => { called = true; },
@@ -367,7 +465,7 @@ describe("AgenticService.create", () => {
367
465
  const service = create({
368
466
  description: "Test",
369
467
  interface: {
370
- sync: { description: "Sync" },
468
+ sync: { type: "action", description: "Sync" },
371
469
  },
372
470
  implementation: {
373
471
  sync: () => { called = true; },
@@ -380,7 +478,7 @@ describe("AgenticService.create", () => {
380
478
  const service = create({
381
479
  description: "Test",
382
480
  interface: {
383
- heal: { description: "Heal", input: { type: "number" } },
481
+ heal: { type: "action", description: "Heal", input: { type: "number" } },
384
482
  },
385
483
  implementation: {
386
484
  heal: async () => { },
@@ -403,7 +501,7 @@ describe("AgenticService.create", () => {
403
501
  const service = create({
404
502
  description: "Test",
405
503
  interface: {
406
- fail: { description: "Fail" },
504
+ fail: { type: "action", description: "Fail" },
407
505
  },
408
506
  implementation: {
409
507
  fail: async () => "something went wrong",
@@ -418,7 +516,7 @@ describe("AgenticService.create", () => {
418
516
  const service = create({
419
517
  description: "Test",
420
518
  interface: {
421
- heal: { description: "Heal", input: { type: "number" } },
519
+ heal: { type: "action", description: "Heal", input: { type: "number" } },
422
520
  },
423
521
  implementation: {
424
522
  heal: async () => { called = true; },
@@ -443,8 +541,8 @@ describe("AgenticService.create", () => {
443
541
  const service = create({
444
542
  description: "Test",
445
543
  interface: {
446
- health: { type: "number", description: "Health" },
447
- name: { type: "string", description: "Name" },
544
+ health: { type: "state", schema: { type: "number" }, description: "Health" },
545
+ name: { type: "state", schema: { type: "string" }, description: "Name" },
448
546
  },
449
547
  implementation: {
450
548
  health: db.observe.resources.health,
@@ -470,8 +568,8 @@ describe("AgenticService.create", () => {
470
568
  const service = create({
471
569
  description: "Test",
472
570
  interface: {
473
- heal: { description: "Heal", input: { type: "number" } },
474
- reset: { description: "Reset" },
571
+ heal: { type: "action", description: "Heal", input: { type: "number" } },
572
+ reset: { type: "action", description: "Reset" },
475
573
  },
476
574
  implementation: {
477
575
  heal: async () => { },
@@ -498,7 +596,7 @@ describe("AgenticService.create", () => {
498
596
  const service = create({
499
597
  description: "Test",
500
598
  interface: {
501
- heal: { description: "Heal", input: { type: "number" } },
599
+ heal: { type: "action", description: "Heal", input: { type: "number" } },
502
600
  },
503
601
  implementation: {
504
602
  heal: async (input) => { received = input; },
@@ -513,7 +611,7 @@ describe("AgenticService.create", () => {
513
611
  const service = create({
514
612
  description: "Test",
515
613
  interface: {
516
- reset: { description: "Reset" },
614
+ reset: { type: "action", description: "Reset" },
517
615
  },
518
616
  implementation: {
519
617
  reset: async () => { called = true; },
@@ -527,7 +625,7 @@ describe("AgenticService.create", () => {
527
625
  const service = create({
528
626
  description: "Test",
529
627
  interface: {
530
- fail: { description: "Fail" },
628
+ fail: { type: "action", description: "Fail" },
531
629
  },
532
630
  implementation: {
533
631
  fail: async () => "bound error",