digital-tools 2.1.3 → 2.3.0

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 (294) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +2 -0
  3. package/dist/client.d.ts +109 -0
  4. package/dist/client.d.ts.map +1 -0
  5. package/dist/client.js +69 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/define.d.ts +2 -2
  8. package/dist/define.d.ts.map +1 -1
  9. package/dist/define.js +21 -11
  10. package/dist/define.js.map +1 -1
  11. package/dist/function-ref.d.ts +229 -0
  12. package/dist/function-ref.d.ts.map +1 -0
  13. package/dist/function-ref.js +28 -0
  14. package/dist/function-ref.js.map +1 -0
  15. package/dist/function-sugar.d.ts +57 -0
  16. package/dist/function-sugar.d.ts.map +1 -0
  17. package/dist/function-sugar.js +79 -0
  18. package/dist/function-sugar.js.map +1 -0
  19. package/dist/index.d.ts +10 -3
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +24 -4
  22. package/dist/index.js.map +1 -1
  23. package/dist/providers/analytics/mixpanel.d.ts.map +1 -1
  24. package/dist/providers/analytics/mixpanel.js +21 -18
  25. package/dist/providers/analytics/mixpanel.js.map +1 -1
  26. package/dist/providers/calendar/cal-com.d.ts.map +1 -1
  27. package/dist/providers/calendar/cal-com.js +10 -10
  28. package/dist/providers/calendar/cal-com.js.map +1 -1
  29. package/dist/providers/calendar/google-calendar.d.ts.map +1 -1
  30. package/dist/providers/calendar/google-calendar.js +4 -4
  31. package/dist/providers/calendar/google-calendar.js.map +1 -1
  32. package/dist/providers/crm/hubspot.d.ts.map +1 -1
  33. package/dist/providers/crm/hubspot.js +107 -85
  34. package/dist/providers/crm/hubspot.js.map +1 -1
  35. package/dist/providers/development/github.d.ts.map +1 -1
  36. package/dist/providers/development/github.js +40 -43
  37. package/dist/providers/development/github.js.map +1 -1
  38. package/dist/providers/ecommerce/shopify.d.ts.map +1 -1
  39. package/dist/providers/ecommerce/shopify.js +79 -62
  40. package/dist/providers/ecommerce/shopify.js.map +1 -1
  41. package/dist/providers/email/resend.d.ts.map +1 -1
  42. package/dist/providers/email/resend.js +20 -16
  43. package/dist/providers/email/resend.js.map +1 -1
  44. package/dist/providers/email/sendgrid.d.ts.map +1 -1
  45. package/dist/providers/email/sendgrid.js +12 -9
  46. package/dist/providers/email/sendgrid.js.map +1 -1
  47. package/dist/providers/finance/stripe.d.ts.map +1 -1
  48. package/dist/providers/finance/stripe.js +44 -42
  49. package/dist/providers/finance/stripe.js.map +1 -1
  50. package/dist/providers/forms/typeform.d.ts.map +1 -1
  51. package/dist/providers/forms/typeform.js +68 -58
  52. package/dist/providers/forms/typeform.js.map +1 -1
  53. package/dist/providers/knowledge/notion.d.ts.map +1 -1
  54. package/dist/providers/knowledge/notion.js +75 -41
  55. package/dist/providers/knowledge/notion.js.map +1 -1
  56. package/dist/providers/marketing/mailchimp.d.ts.map +1 -1
  57. package/dist/providers/marketing/mailchimp.js +74 -61
  58. package/dist/providers/marketing/mailchimp.js.map +1 -1
  59. package/dist/providers/media/cloudinary.d.ts.map +1 -1
  60. package/dist/providers/media/cloudinary.js +30 -28
  61. package/dist/providers/media/cloudinary.js.map +1 -1
  62. package/dist/providers/messaging/slack.d.ts.map +1 -1
  63. package/dist/providers/messaging/slack.js +75 -58
  64. package/dist/providers/messaging/slack.js.map +1 -1
  65. package/dist/providers/messaging/twilio-sms.d.ts.map +1 -1
  66. package/dist/providers/messaging/twilio-sms.js +33 -15
  67. package/dist/providers/messaging/twilio-sms.js.map +1 -1
  68. package/dist/providers/project-management/linear.d.ts.map +1 -1
  69. package/dist/providers/project-management/linear.js +31 -27
  70. package/dist/providers/project-management/linear.js.map +1 -1
  71. package/dist/providers/spreadsheet/google-sheets.d.ts.map +1 -1
  72. package/dist/providers/spreadsheet/google-sheets.js +21 -18
  73. package/dist/providers/spreadsheet/google-sheets.js.map +1 -1
  74. package/dist/providers/spreadsheet/xlsx.d.ts.map +1 -1
  75. package/dist/providers/spreadsheet/xlsx.js +4 -4
  76. package/dist/providers/spreadsheet/xlsx.js.map +1 -1
  77. package/dist/providers/storage/index.js +1 -0
  78. package/dist/providers/storage/index.js.map +1 -1
  79. package/dist/providers/storage/s3.d.ts.map +1 -1
  80. package/dist/providers/storage/s3.js +36 -27
  81. package/dist/providers/storage/s3.js.map +1 -1
  82. package/dist/providers/support/zendesk.d.ts.map +1 -1
  83. package/dist/providers/support/zendesk.js +24 -25
  84. package/dist/providers/support/zendesk.js.map +1 -1
  85. package/dist/providers/tasks/todoist.d.ts.map +1 -1
  86. package/dist/providers/tasks/todoist.js +18 -18
  87. package/dist/providers/tasks/todoist.js.map +1 -1
  88. package/dist/providers/video-conferencing/google-meet.d.ts.map +1 -1
  89. package/dist/providers/video-conferencing/google-meet.js +11 -11
  90. package/dist/providers/video-conferencing/google-meet.js.map +1 -1
  91. package/dist/providers/video-conferencing/jitsi.js +14 -14
  92. package/dist/providers/video-conferencing/jitsi.js.map +1 -1
  93. package/dist/providers/video-conferencing/teams.d.ts.map +1 -1
  94. package/dist/providers/video-conferencing/teams.js +9 -7
  95. package/dist/providers/video-conferencing/teams.js.map +1 -1
  96. package/dist/providers/video-conferencing/zoom.d.ts.map +1 -1
  97. package/dist/providers/video-conferencing/zoom.js +26 -24
  98. package/dist/providers/video-conferencing/zoom.js.map +1 -1
  99. package/dist/tools/data.d.ts.map +1 -1
  100. package/dist/tools/data.js +5 -12
  101. package/dist/tools/data.js.map +1 -1
  102. package/dist/tools/index.d.ts +1 -0
  103. package/dist/tools/index.d.ts.map +1 -1
  104. package/dist/tools/index.js +1 -0
  105. package/dist/tools/index.js.map +1 -1
  106. package/dist/tools/system.d.ts +289 -0
  107. package/dist/tools/system.d.ts.map +1 -0
  108. package/dist/tools/system.js +752 -0
  109. package/dist/tools/system.js.map +1 -0
  110. package/dist/tools/web.d.ts.map +1 -1
  111. package/dist/tools/web.js +22 -10
  112. package/dist/tools/web.js.map +1 -1
  113. package/dist/track-record.d.ts +101 -0
  114. package/dist/track-record.d.ts.map +1 -0
  115. package/dist/track-record.js +17 -0
  116. package/dist/track-record.js.map +1 -0
  117. package/dist/types.d.ts +210 -9
  118. package/dist/types.d.ts.map +1 -1
  119. package/dist/verb-registration.d.ts +122 -0
  120. package/dist/verb-registration.d.ts.map +1 -0
  121. package/dist/verb-registration.js +176 -0
  122. package/dist/verb-registration.js.map +1 -0
  123. package/dist/worker.d.ts +93 -0
  124. package/dist/worker.d.ts.map +1 -0
  125. package/dist/worker.js +315 -0
  126. package/dist/worker.js.map +1 -0
  127. package/dist/wrap.d.ts +89 -0
  128. package/dist/wrap.d.ts.map +1 -0
  129. package/dist/wrap.js +225 -0
  130. package/dist/wrap.js.map +1 -0
  131. package/package.json +31 -14
  132. package/src/client.ts +136 -0
  133. package/src/define.ts +30 -24
  134. package/src/function-ref.ts +264 -0
  135. package/src/function-sugar.ts +134 -0
  136. package/src/index.ts +132 -10
  137. package/src/providers/analytics/mixpanel.ts +19 -18
  138. package/src/providers/calendar/cal-com.ts +29 -18
  139. package/src/providers/calendar/google-calendar.ts +20 -14
  140. package/src/providers/crm/hubspot.ts +225 -99
  141. package/src/providers/development/github.ts +206 -135
  142. package/src/providers/ecommerce/shopify.ts +250 -89
  143. package/src/providers/email/resend.ts +101 -28
  144. package/src/providers/email/sendgrid.ts +12 -9
  145. package/src/providers/finance/stripe.ts +128 -49
  146. package/src/providers/forms/typeform.ts +74 -58
  147. package/src/providers/knowledge/notion.ts +340 -88
  148. package/src/providers/marketing/mailchimp.ts +86 -70
  149. package/src/providers/media/cloudinary.ts +99 -41
  150. package/src/providers/messaging/slack.ts +283 -85
  151. package/src/providers/messaging/twilio-sms.ts +35 -15
  152. package/src/providers/project-management/linear.ts +143 -55
  153. package/src/providers/spreadsheet/google-sheets.ts +222 -56
  154. package/src/providers/spreadsheet/xlsx.ts +47 -16
  155. package/src/providers/storage/s3.ts +119 -47
  156. package/src/providers/support/zendesk.ts +196 -46
  157. package/src/providers/tasks/todoist.ts +20 -26
  158. package/src/providers/video-conferencing/google-meet.ts +17 -20
  159. package/src/providers/video-conferencing/jitsi.ts +14 -14
  160. package/src/providers/video-conferencing/teams.ts +14 -13
  161. package/src/providers/video-conferencing/zoom.ts +54 -49
  162. package/src/tools/data.ts +6 -16
  163. package/src/tools/index.ts +1 -0
  164. package/src/tools/system.ts +887 -0
  165. package/src/tools/web.ts +22 -10
  166. package/src/track-record.ts +106 -0
  167. package/src/types.ts +241 -13
  168. package/src/verb-registration.ts +197 -0
  169. package/src/worker.ts +370 -0
  170. package/src/wrap.ts +260 -0
  171. package/test/client.test.ts +146 -0
  172. package/test/communication-tools-extended.test.ts +734 -0
  173. package/test/data-tools-extended.test.ts +743 -0
  174. package/test/define-extended.test.ts +819 -0
  175. package/test/define.test.ts +150 -41
  176. package/test/entities.test.ts +623 -0
  177. package/test/extended-entities.test.ts +1228 -0
  178. package/test/provider-implementations.test.ts +725 -0
  179. package/test/provider-registry-extended.test.ts +583 -0
  180. package/test/providers/google-sheets.test.ts +851 -0
  181. package/test/providers/helpers.ts +554 -0
  182. package/test/providers/hubspot.test.ts +576 -0
  183. package/test/providers/slack.test.ts +932 -0
  184. package/test/providers/stripe.test.ts +701 -0
  185. package/test/providers.test.ts +578 -0
  186. package/test/system-tools-extended.test.ts +632 -0
  187. package/test/system.test.ts +673 -0
  188. package/test/tools.test.ts +15 -11
  189. package/test/types.test.ts +402 -0
  190. package/test/verb-registration.test.ts +395 -0
  191. package/test/web-tools.test.ts +553 -0
  192. package/test/worker-extended.test.ts +699 -0
  193. package/test/worker.test.ts +576 -0
  194. package/test/wrap.test.ts +366 -0
  195. package/tsconfig.json +3 -13
  196. package/vitest.config.ts +37 -0
  197. package/wrangler.jsonc +9 -0
  198. package/.turbo/turbo-build.log +0 -4
  199. package/LICENSE +0 -21
  200. package/dist/providers/voice/vapi.d.ts +0 -27
  201. package/dist/providers/voice/vapi.d.ts.map +0 -1
  202. package/dist/providers/voice/vapi.js +0 -440
  203. package/dist/providers/voice/vapi.js.map +0 -1
  204. package/src/define.js +0 -259
  205. package/src/entities/advertising.js +0 -999
  206. package/src/entities/ai.js +0 -756
  207. package/src/entities/analytics.js +0 -1588
  208. package/src/entities/automation.js +0 -601
  209. package/src/entities/communication.js +0 -1150
  210. package/src/entities/crm.js +0 -1386
  211. package/src/entities/design.js +0 -546
  212. package/src/entities/development.js +0 -2212
  213. package/src/entities/document.js +0 -874
  214. package/src/entities/ecommerce.js +0 -1429
  215. package/src/entities/experiment.js +0 -1039
  216. package/src/entities/finance.js +0 -3478
  217. package/src/entities/forms.js +0 -1892
  218. package/src/entities/hr.js +0 -661
  219. package/src/entities/identity.js +0 -997
  220. package/src/entities/index.js +0 -282
  221. package/src/entities/infrastructure.js +0 -1153
  222. package/src/entities/knowledge.js +0 -1438
  223. package/src/entities/marketing.js +0 -1610
  224. package/src/entities/media.js +0 -1634
  225. package/src/entities/notification.js +0 -1199
  226. package/src/entities/presentation.js +0 -1274
  227. package/src/entities/productivity.js +0 -1317
  228. package/src/entities/project-management.js +0 -1136
  229. package/src/entities/recruiting.js +0 -736
  230. package/src/entities/shipping.js +0 -509
  231. package/src/entities/signature.js +0 -1102
  232. package/src/entities/site.js +0 -222
  233. package/src/entities/spreadsheet.js +0 -1341
  234. package/src/entities/storage.js +0 -1198
  235. package/src/entities/support.js +0 -1166
  236. package/src/entities/video-conferencing.js +0 -1750
  237. package/src/entities/video.js +0 -950
  238. package/src/entities.js +0 -1663
  239. package/src/index.js +0 -74
  240. package/src/providers/analytics/index.js +0 -17
  241. package/src/providers/analytics/mixpanel.js +0 -255
  242. package/src/providers/calendar/cal-com.js +0 -303
  243. package/src/providers/calendar/google-calendar.js +0 -335
  244. package/src/providers/calendar/index.js +0 -20
  245. package/src/providers/crm/hubspot.js +0 -566
  246. package/src/providers/crm/index.js +0 -17
  247. package/src/providers/development/github.js +0 -472
  248. package/src/providers/development/index.js +0 -17
  249. package/src/providers/ecommerce/index.js +0 -17
  250. package/src/providers/ecommerce/shopify.js +0 -378
  251. package/src/providers/email/index.js +0 -20
  252. package/src/providers/email/resend.js +0 -258
  253. package/src/providers/email/sendgrid.js +0 -161
  254. package/src/providers/finance/index.js +0 -17
  255. package/src/providers/finance/stripe.js +0 -549
  256. package/src/providers/forms/index.js +0 -17
  257. package/src/providers/forms/typeform.js +0 -500
  258. package/src/providers/index.js +0 -123
  259. package/src/providers/knowledge/index.js +0 -17
  260. package/src/providers/knowledge/notion.js +0 -389
  261. package/src/providers/marketing/index.js +0 -17
  262. package/src/providers/marketing/mailchimp.js +0 -443
  263. package/src/providers/media/cloudinary.js +0 -318
  264. package/src/providers/media/index.js +0 -17
  265. package/src/providers/messaging/index.js +0 -20
  266. package/src/providers/messaging/slack.js +0 -393
  267. package/src/providers/messaging/twilio-sms.js +0 -249
  268. package/src/providers/project-management/index.js +0 -17
  269. package/src/providers/project-management/linear.js +0 -575
  270. package/src/providers/registry.js +0 -86
  271. package/src/providers/spreadsheet/google-sheets.js +0 -375
  272. package/src/providers/spreadsheet/index.js +0 -20
  273. package/src/providers/spreadsheet/xlsx.js +0 -423
  274. package/src/providers/storage/index.js +0 -24
  275. package/src/providers/storage/s3.js +0 -419
  276. package/src/providers/support/index.js +0 -17
  277. package/src/providers/support/zendesk.js +0 -373
  278. package/src/providers/tasks/index.js +0 -17
  279. package/src/providers/tasks/todoist.js +0 -286
  280. package/src/providers/types.js +0 -9
  281. package/src/providers/video-conferencing/google-meet.js +0 -286
  282. package/src/providers/video-conferencing/index.js +0 -31
  283. package/src/providers/video-conferencing/jitsi.js +0 -254
  284. package/src/providers/video-conferencing/teams.js +0 -270
  285. package/src/providers/video-conferencing/zoom.js +0 -332
  286. package/src/registry.js +0 -128
  287. package/src/tools/communication.js +0 -184
  288. package/src/tools/data.js +0 -205
  289. package/src/tools/index.js +0 -11
  290. package/src/tools/web.js +0 -137
  291. package/src/types.js +0 -10
  292. package/test/define.test.js +0 -306
  293. package/test/registry.test.js +0 -357
  294. package/test/tools.test.js +0 -363
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "digital-tools",
3
- "version": "2.1.3",
3
+ "version": "2.3.0",
4
4
  "description": "Tools that can be used by both humans and AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -17,12 +17,37 @@
17
17
  "./registry": {
18
18
  "import": "./dist/registry.js",
19
19
  "types": "./dist/registry.d.ts"
20
+ },
21
+ "./worker": {
22
+ "import": "./dist/worker.js",
23
+ "types": "./dist/worker.d.ts"
24
+ },
25
+ "./client": {
26
+ "import": "./dist/client.js",
27
+ "types": "./dist/client.d.ts"
20
28
  }
21
29
  },
30
+ "scripts": {
31
+ "build": "tsc",
32
+ "dev": "tsc --watch",
33
+ "test": "vitest run",
34
+ "typecheck": "tsc --noEmit",
35
+ "lint": "eslint .",
36
+ "clean": "rm -rf dist"
37
+ },
22
38
  "dependencies": {
23
- "zod": "^3.23.0",
24
- "ai-database": "2.1.3",
25
- "ai-functions": "2.1.3"
39
+ "ai-database": "2.3.0",
40
+ "ai-functions": "2.3.0",
41
+ "autonomous-finance": "0.0.1",
42
+ "digital-objects": "1.1.0",
43
+ "id.org.ai": "^0.3.0",
44
+ "rpc.do": "^0.1.0",
45
+ "zod": "^3.23.0"
46
+ },
47
+ "devDependencies": {
48
+ "@cloudflare/vitest-pool-workers": "^0.8.0",
49
+ "@cloudflare/workers-types": "^4.20250109.0",
50
+ "vitest": "^2.1.0"
26
51
  },
27
52
  "keywords": [
28
53
  "ai",
@@ -32,13 +57,5 @@
32
57
  "primitives",
33
58
  "mcp"
34
59
  ],
35
- "license": "MIT",
36
- "scripts": {
37
- "build": "tsc",
38
- "dev": "tsc --watch",
39
- "test": "vitest",
40
- "typecheck": "tsc --noEmit",
41
- "lint": "eslint .",
42
- "clean": "rm -rf dist"
43
- }
44
- }
60
+ "license": "MIT"
61
+ }
package/src/client.ts ADDED
@@ -0,0 +1,136 @@
1
+ /**
2
+ * RPC Client for digital-tools worker
3
+ *
4
+ * Connects to a deployed digital-tools worker using rpc.do,
5
+ * providing a fully typed client for tool discovery, execution,
6
+ * and MCP conversion.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { createToolClient } from 'digital-tools/client'
11
+ *
12
+ * const client = createToolClient('https://digital-tools.workers.dev')
13
+ * const tools = await client.list()
14
+ * const result = await client.executeTool('data.json.parse', { text: '{}' })
15
+ * ```
16
+ *
17
+ * @packageDocumentation
18
+ */
19
+
20
+ import { RPC, http } from 'rpc.do'
21
+ import type { AnyTool, ToolCategory, ToolQuery, MCPTool, ToolSubcategory } from './types.js'
22
+
23
+ /**
24
+ * Client options for connecting to a digital-tools worker
25
+ */
26
+ export interface ToolClientOptions {
27
+ /** Authentication token */
28
+ token?: string
29
+ /** Custom headers */
30
+ headers?: Record<string, string>
31
+ }
32
+
33
+ /**
34
+ * API type matching ToolServiceCore's public RPC methods
35
+ *
36
+ * This interface mirrors the public methods of ToolServiceCore
37
+ * from the worker, providing a typed contract for the RPC client.
38
+ */
39
+ export interface ToolServiceAPI {
40
+ /** Register a tool in the service registry */
41
+ register(tool: AnyTool): void
42
+
43
+ /** Unregister a tool */
44
+ unregister(id: string): boolean
45
+
46
+ /** Get a tool by ID */
47
+ get(id: string): AnyTool | undefined
48
+
49
+ /** Check if a tool exists */
50
+ has(id: string): boolean
51
+
52
+ /** List all tool IDs */
53
+ list(): string[]
54
+
55
+ /** Query tools with filtering */
56
+ query(options: ToolQuery): AnyTool[]
57
+
58
+ /** Get tools by category */
59
+ byCategory(category: ToolCategory): AnyTool[]
60
+
61
+ /** Execute a tool by ID */
62
+ executeTool<TInput = unknown, TOutput = unknown>(id: string, input: TInput): Promise<TOutput>
63
+
64
+ /** Convert a tool to MCP format */
65
+ toMCP(tool: AnyTool): MCPTool
66
+
67
+ /** Convert all registered tools to MCP format */
68
+ listMCPTools(): MCPTool[]
69
+
70
+ /** Import a tool from MCP format */
71
+ fromMCP(
72
+ mcpTool: MCPTool,
73
+ handler: (input: unknown) => unknown | Promise<unknown>,
74
+ options?: {
75
+ category?: ToolCategory
76
+ subcategory?: ToolSubcategory
77
+ tags?: string[]
78
+ }
79
+ ): AnyTool
80
+
81
+ /** Clear all tools */
82
+ clear(keepBuiltins?: boolean): void
83
+ }
84
+
85
+ /** Default worker URL for digital-tools */
86
+ const DEFAULT_URL = 'https://digital-tools.workers.dev'
87
+
88
+ /**
89
+ * Create an RPC client that connects to a deployed digital-tools worker
90
+ *
91
+ * @param url - The URL of the deployed digital-tools worker
92
+ * @param options - Optional client configuration
93
+ * @returns A typed RPC client for the tool service
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * import { createToolClient } from 'digital-tools/client'
98
+ *
99
+ * // Connect to default worker
100
+ * const client = createToolClient()
101
+ *
102
+ * // Connect to custom deployment
103
+ * const client = createToolClient('https://my-tools.example.com')
104
+ *
105
+ * // List available tools
106
+ * const toolIds = await client.list()
107
+ *
108
+ * // Execute a tool
109
+ * const result = await client.executeTool('data.json.parse', { text: '{"key": "value"}' })
110
+ *
111
+ * // Query tools by category
112
+ * const dataTools = await client.query({ category: 'data' })
113
+ *
114
+ * // Get MCP-compatible tool definitions
115
+ * const mcpTools = await client.listMCPTools()
116
+ * ```
117
+ */
118
+ export function createToolClient(url: string = DEFAULT_URL, options?: ToolClientOptions) {
119
+ const token = options?.token
120
+ return RPC<ToolServiceAPI>(http(url, token))
121
+ }
122
+
123
+ /**
124
+ * Default RPC client connected to the standard digital-tools worker deployment
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * import client from 'digital-tools/client'
129
+ *
130
+ * const tools = await client.list()
131
+ * const result = await client.executeTool('data.json.parse', { text: '{}' })
132
+ * ```
133
+ */
134
+ const client = createToolClient()
135
+
136
+ export default client
package/src/define.ts CHANGED
@@ -56,8 +56,7 @@ function schemaToParameters(schema: Schema): ToolParameter[] {
56
56
 
57
57
  return Object.entries(jsonSchema.properties).map(([name, propSchema]) => ({
58
58
  name,
59
- description:
60
- (propSchema as JSONSchema).description || `Parameter: ${name}`,
59
+ description: (propSchema as JSONSchema).description || `Parameter: ${name}`,
61
60
  schema: propSchema as JSONSchema,
62
61
  required: required.includes(name),
63
62
  default: (propSchema as JSONSchema).default,
@@ -99,14 +98,22 @@ export function defineTool<TInput, TOutput>(
99
98
  name: options.name,
100
99
  description: options.description,
101
100
  category: options.category,
102
- subcategory: options.subcategory,
101
+ ...(options.subcategory !== undefined && { subcategory: options.subcategory }),
102
+ // SVO co-design (aip-oejp) — all optional. Verb auto-registration in
103
+ // digital-objects is intentionally deferred (see bead aip-oejp follow-up):
104
+ // we store the metadata here so dispatchers can resolve it, but we don't
105
+ // mutate the cross-package Verb registry from a `defineTool()` side-effect.
106
+ ...(options.verb !== undefined && { verb: options.verb }),
107
+ ...(options.frame !== undefined && { frame: options.frame }),
108
+ ...(options.auth !== undefined && { auth: options.auth }),
109
+ ...(options.pricing !== undefined && { pricing: options.pricing }),
103
110
  parameters: schemaToParameters(options.input),
104
- output: options.output
105
- ? {
106
- description: 'Tool output',
107
- schema: options.output,
108
- }
109
- : undefined,
111
+ ...(options.output && {
112
+ output: {
113
+ description: 'Tool output',
114
+ schema: options.output,
115
+ },
116
+ }),
110
117
  handler: options.handler,
111
118
  ...options.options,
112
119
  }
@@ -147,10 +154,7 @@ export function createToolExecutor(context: ToolContext) {
147
154
  /**
148
155
  * Execute a tool by ID with context tracking
149
156
  */
150
- async execute<TInput, TOutput>(
151
- toolId: string,
152
- input: TInput
153
- ): Promise<ToolResult<TOutput>> {
157
+ async execute<TInput, TOutput>(toolId: string, input: TInput): Promise<ToolResult<TOutput>> {
154
158
  const tool = registry.get(toolId)
155
159
 
156
160
  if (!tool) {
@@ -164,11 +168,7 @@ export function createToolExecutor(context: ToolContext) {
164
168
  }
165
169
 
166
170
  // Check audience restrictions
167
- if (
168
- tool.audience &&
169
- tool.audience !== 'both' &&
170
- tool.audience !== context.executor.type
171
- ) {
171
+ if (tool.audience && tool.audience !== 'both' && tool.audience !== context.executor.type) {
172
172
  return {
173
173
  success: false,
174
174
  error: {
@@ -189,7 +189,7 @@ export function createToolExecutor(context: ToolContext) {
189
189
  data,
190
190
  metadata: {
191
191
  duration,
192
- requestId: context.requestId,
192
+ ...(context.requestId !== undefined && { requestId: context.requestId }),
193
193
  },
194
194
  }
195
195
  } catch (error) {
@@ -204,7 +204,7 @@ export function createToolExecutor(context: ToolContext) {
204
204
  },
205
205
  metadata: {
206
206
  duration,
207
- requestId: context.requestId,
207
+ ...(context.requestId !== undefined && { requestId: context.requestId }),
208
208
  },
209
209
  }
210
210
  }
@@ -264,7 +264,7 @@ export function toolBuilder(id: string) {
264
264
  return this
265
265
  },
266
266
 
267
- subcategory(subcategory: DefineToolOptions<unknown, unknown>['subcategory']) {
267
+ subcategory(subcategory: NonNullable<DefineToolOptions<unknown, unknown>['subcategory']>) {
268
268
  config.subcategory = subcategory
269
269
  return this
270
270
  },
@@ -288,13 +288,19 @@ export function toolBuilder(id: string) {
288
288
  }
289
289
  },
290
290
 
291
- options(opts: Partial<AnyTool>) {
292
- config.options = opts as DefineToolOptions<unknown, unknown>['options']
291
+ options(opts: NonNullable<DefineToolOptions<unknown, unknown>['options']>) {
292
+ config.options = opts
293
293
  return this
294
294
  },
295
295
 
296
296
  build<TInput, TOutput>(): Tool<TInput, TOutput> {
297
- if (!config.name || !config.description || !config.category || !config.input || !config.handler) {
297
+ if (
298
+ !config.name ||
299
+ !config.description ||
300
+ !config.category ||
301
+ !config.input ||
302
+ !config.handler
303
+ ) {
298
304
  throw new Error('Tool requires id, name, description, category, input, and handler')
299
305
  }
300
306
  return defineTool(config as DefineToolOptions<TInput, TOutput>)
@@ -0,0 +1,264 @@
1
+ /**
2
+ * FunctionRef — discriminated union of the Four Functions (v3 §6).
3
+ *
4
+ * The Services-as-Software book decomposes any cascade step into one of four
5
+ * Function kinds:
6
+ *
7
+ * - `code` — deterministic, programmable. A pure function or a
8
+ * reference to a code Action.
9
+ * - `generative` — single-shot LLM call (no tool use, no agentic loop).
10
+ * - `agentic` — looping, tool-using AI worker with a persona.
11
+ * - `human` — work that *must* be performed by a person, with a
12
+ * declared rationale + expiration policy describing the
13
+ * conditions under which the Function may be migrated to
14
+ * a non-human kind.
15
+ *
16
+ * Each kind carries the same per-Function knobs the v3 design pins:
17
+ * a {@link RewardSignal} reference, a {@link CostModel}, an
18
+ * {@link OversightPolicy}, and a {@link TrackRecord}. The agentic and human
19
+ * variants additionally carry kind-specific shape (mode/persona vs.
20
+ * rationale/expiration).
21
+ *
22
+ * This type is *additive* alongside the legacy {@link Tool} interface.
23
+ * Existing tools are not migrated; new SaS cascades use `FunctionRef` directly.
24
+ *
25
+ * @packageDocumentation
26
+ */
27
+
28
+ import type { ActionRef } from 'digital-objects'
29
+ import type { CostModel } from 'autonomous-finance'
30
+ import type { AgentMode, PromotionPolicy, TrackRecord } from './track-record.js'
31
+
32
+ // ============================================================================
33
+ // Reward (placeholder)
34
+ // ============================================================================
35
+
36
+ /**
37
+ * Placeholder reward signal — the canonical type ships in `business-as-code`
38
+ * once the OKR / KeyResult primitives land. Until then, a Function declares
39
+ * the KeyResult it should be rewarded against by reference.
40
+ *
41
+ * The string form is an MDXLD `$id` that resolves to a KeyResult node in the
42
+ * BaC graph (e.g. `'kr:revenue.q3.signups'`).
43
+ */
44
+ export interface RewardSignal {
45
+ /** `$id` of the KeyResult the Function moves the needle on. */
46
+ keyResultRef: string
47
+ }
48
+
49
+ // ============================================================================
50
+ // Oversight + per-Function knobs shared across all four kinds
51
+ // ============================================================================
52
+
53
+ /**
54
+ * Reusable persona reference for an agentic Function — typically a string
55
+ * `$id` into the persona library shipped by `ai-evaluate`. Kept opaque here
56
+ * to avoid a layer-up dependency on `ai-evaluate`.
57
+ */
58
+ export type PersonaRef = string
59
+
60
+ /**
61
+ * Sign-off policy on the output of an agentic Function before it is released
62
+ * downstream. `none` means no per-invocation sign-off (autonomous);
63
+ * `self` means the Function reviews its own output against its persona;
64
+ * `peer` requires another agentic Function to approve; `human` requires a
65
+ * human reviewer; `panel` defers to an EvaluatorPanel reference.
66
+ */
67
+ export type SignOffMode = 'none' | 'self' | 'peer' | 'human' | 'panel'
68
+
69
+ /**
70
+ * Declarative oversight gate applied per-invocation to a Function.
71
+ *
72
+ * `mode` selects the {@link AgentMode} the Function is currently operating in;
73
+ * `promotion` (when present) is the {@link PromotionPolicy} that may move the
74
+ * Function up or down the ladder based on its {@link TrackRecord}.
75
+ */
76
+ export interface OversightPolicy {
77
+ mode: AgentMode
78
+ promotion?: PromotionPolicy
79
+ }
80
+
81
+ // ============================================================================
82
+ // Discriminated union
83
+ // ============================================================================
84
+
85
+ /**
86
+ * Fields common to every Function kind. The discriminator (`kind`) is added
87
+ * by each variant so consumers can narrow with a single switch.
88
+ */
89
+ export interface BaseFunctionRef {
90
+ /** MDXLD `$id` (e.g. `'fn:code:summarize'`). */
91
+ $id: string
92
+ /** Human-readable name used by the registry / UI. */
93
+ name: string
94
+ /** Optional one-line description rendered by catalog UIs. */
95
+ description?: string
96
+ /** Reward this Function is optimised against, when known. */
97
+ reward?: RewardSignal
98
+ /** Declared cost model — used for budgeting and price quoting. */
99
+ costModel?: CostModel
100
+ /** Per-Function oversight + autonomy policy. */
101
+ oversight?: OversightPolicy
102
+ /**
103
+ * Accumulated quality / cost / override signal. Present on materialised
104
+ * (registered) Functions; omitted on the spec passed to a sugar factory.
105
+ */
106
+ track?: TrackRecord
107
+ }
108
+
109
+ /**
110
+ * Code Function — deterministic, programmable.
111
+ *
112
+ * `handler` may be either an inline function (preferred for in-process
113
+ * Services) or an {@link ActionRef} string pointing at a registered Action
114
+ * in `digital-objects` (preferred for cross-process / serialisable cascades).
115
+ */
116
+ export interface CodeFunctionRef<TInput = unknown, TOutput = unknown> extends BaseFunctionRef {
117
+ kind: 'code'
118
+ handler: ((input: TInput) => TOutput | Promise<TOutput>) | ActionRef
119
+ }
120
+
121
+ /**
122
+ * Generative Function — single-shot LLM call. No tool use, no loop.
123
+ *
124
+ * `modelHint` is an optional preferred model spec (e.g. `'claude-opus-4'`);
125
+ * the runtime is free to substitute based on cost/availability policy.
126
+ *
127
+ * `outputSchema` (round-13) is an optional per-step output contract used by
128
+ * the cascade walker to constrain mid-cascade Generative output. When
129
+ * present, the walker passes it to `ai-functions.generateObject` for that
130
+ * step; when absent, the walker falls back to the Service's
131
+ * `schema.output` for the LAST Generative step and `z.string()` for earlier
132
+ * ones. Typed as `unknown` because the canonical Standard Schema type lives
133
+ * in `services-as-software` and `digital-tools` shouldn't pull that
134
+ * dependency — call sites cast to their concrete schema type.
135
+ */
136
+ export interface GenerativeFunctionRef extends BaseFunctionRef {
137
+ kind: 'generative'
138
+ modelHint?: string
139
+ outputSchema?: unknown
140
+ }
141
+
142
+ /**
143
+ * Agentic Function — looping, tool-using AI worker with a persona and a
144
+ * sign-off rule.
145
+ */
146
+ export interface AgenticFunctionRef extends BaseFunctionRef {
147
+ kind: 'agentic'
148
+ /**
149
+ * Operating mode for this Function specifically. Mirrors
150
+ * {@link OversightPolicy.mode}; declared inline because the agentic kind is
151
+ * the primary consumer of the autonomy ladder.
152
+ */
153
+ mode: AgentMode
154
+ /**
155
+ * Optional preferred model spec (e.g. `'claude-opus-4'`, `'sonnet'`).
156
+ * Mirrors {@link GenerativeFunctionRef.modelHint}; the runtime is free to
157
+ * substitute based on cost/availability/track-record policy. The
158
+ * `services-as-software` cascade walker forwards this to
159
+ * `ai-functions.generateText` for the agentic tool-use loop.
160
+ */
161
+ modelHint?: string
162
+ /**
163
+ * Tool ids registered with `digital-tools.defineTool`. Each id resolves to
164
+ * exactly one {@link Tool} via the global registry. Scope-style strings
165
+ * (e.g. `'github.repos'`) are accepted but treated as literal tool ids,
166
+ * not permission scopes — the runtime does not expand `'github.repos'`
167
+ * into the set of github repo tools. True scope→tools resolution
168
+ * (capability-based permission expansion) is round-9+ work; until then,
169
+ * list each concrete tool id explicitly.
170
+ */
171
+ toolPermissions?: string[]
172
+ /**
173
+ * Concurrency policy for this Function:
174
+ *
175
+ * - `number` — hard cap on concurrent invocations (caller picks N).
176
+ * - `'serial'` — one invocation at a time (semantic alias for `1`).
177
+ * - `'fan-out'` — semantic "fan out as wide as upstream emits"; the
178
+ * cascade compiler chooses N at runtime based on the
179
+ * upstream step's emitted batch size and ambient
180
+ * cost-budget policy.
181
+ *
182
+ * Used by the runtime to bound cost on bursty workloads.
183
+ */
184
+ concurrency?: number | 'serial' | 'fan-out'
185
+ /** Persona reference (typically into the `ai-evaluate` persona library). */
186
+ persona?: PersonaRef
187
+ /**
188
+ * Sign-off requirement before the Function's output is released downstream.
189
+ * Defaults to `'none'` for `mode === 'autonomous'`, `'human'` for
190
+ * `mode === 'supervised'`, and `'human'` for `mode === 'manual'`.
191
+ */
192
+ signOff?: SignOffMode
193
+ }
194
+
195
+ /**
196
+ * Reasons a step in the cascade *must* be performed by a human. Drives both
197
+ * UX copy ("why is a human reviewing this?") and the migration policy
198
+ * ({@link HumanFunctionRef.expirationPolicy} declares when the human step
199
+ * may be replaced by a non-human Function).
200
+ */
201
+ export type HumanRationale =
202
+ | 'approval' // sign-off / authorisation cannot be delegated
203
+ | 'physical' // requires a physical action in the world
204
+ | 'regulatory' // law or policy mandates a human in the loop
205
+ | 'trust' // customer pays specifically for human attention
206
+ | 'premium' // human work is the value being sold
207
+
208
+ /**
209
+ * Conditions under which a {@link HumanFunctionRef} may be migrated to a
210
+ * non-human Function (typically an {@link AgenticFunctionRef}).
211
+ *
212
+ * The expiration is *declarative*: the policy is checked against the
213
+ * Function's accumulated {@link TrackRecord} (and any sibling-Function
214
+ * record indicated by `migrateTo`). When the predicate holds, the cascade
215
+ * compiler may swap the Human Function for the named target Function with
216
+ * the catalog operator's confirmation.
217
+ */
218
+ export interface HumanExpirationPolicy {
219
+ /** `$id` of the Function the human step should migrate to once eligible. */
220
+ migrateTo?: string
221
+ /**
222
+ * Migrate when the cascade-without-the-human's accuracy exceeds this
223
+ * threshold (0–1). The bookkeeper case ("could AI have decided this
224
+ * correctly?") is the canonical example — see v3 §14 open decision.
225
+ */
226
+ whenAccuracyExceeds?: number
227
+ /** Migrate when total samples exceed this count. */
228
+ whenSamplesExceed?: number
229
+ }
230
+
231
+ /**
232
+ * Channel used to dispatch work to a human. Opaque string so concrete
233
+ * channels (Slack DM, email, in-app inbox, paging system, …) can be wired
234
+ * by the runtime without a type change here.
235
+ */
236
+ export type HumanChannel = string
237
+
238
+ /**
239
+ * Human Function — work that must be performed by a person.
240
+ *
241
+ * Carries a declared {@link HumanRationale} (so UX can explain *why* a human
242
+ * is in the loop) and an {@link HumanExpirationPolicy} (so the cascade can
243
+ * eventually be migrated to a non-human Function once track-record allows).
244
+ */
245
+ export interface HumanFunctionRef extends BaseFunctionRef {
246
+ kind: 'human'
247
+ rationale: HumanRationale
248
+ expirationPolicy: HumanExpirationPolicy
249
+ channel?: HumanChannel
250
+ }
251
+
252
+ /**
253
+ * Discriminated union of the Four Functions. Narrow with `switch (fn.kind)`.
254
+ */
255
+ export type FunctionRef =
256
+ | CodeFunctionRef
257
+ | GenerativeFunctionRef
258
+ | AgenticFunctionRef
259
+ | HumanFunctionRef
260
+
261
+ /**
262
+ * Discriminator string union — convenient for table-driven dispatchers.
263
+ */
264
+ export type FunctionKind = FunctionRef['kind']