digital-tools 2.1.1 → 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.
- package/CHANGELOG.md +17 -0
- package/README.md +2 -0
- package/dist/client.d.ts +109 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +69 -0
- package/dist/client.js.map +1 -0
- package/dist/define.d.ts +2 -2
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js +22 -20
- package/dist/define.js.map +1 -1
- package/dist/function-ref.d.ts +229 -0
- package/dist/function-ref.d.ts.map +1 -0
- package/dist/function-ref.js +28 -0
- package/dist/function-ref.js.map +1 -0
- package/dist/function-sugar.d.ts +57 -0
- package/dist/function-sugar.d.ts.map +1 -0
- package/dist/function-sugar.js +79 -0
- package/dist/function-sugar.js.map +1 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -4
- package/dist/index.js.map +1 -1
- package/dist/providers/analytics/mixpanel.d.ts.map +1 -1
- package/dist/providers/analytics/mixpanel.js +21 -18
- package/dist/providers/analytics/mixpanel.js.map +1 -1
- package/dist/providers/calendar/cal-com.d.ts.map +1 -1
- package/dist/providers/calendar/cal-com.js +10 -10
- package/dist/providers/calendar/cal-com.js.map +1 -1
- package/dist/providers/calendar/google-calendar.d.ts.map +1 -1
- package/dist/providers/calendar/google-calendar.js +4 -4
- package/dist/providers/calendar/google-calendar.js.map +1 -1
- package/dist/providers/crm/hubspot.d.ts.map +1 -1
- package/dist/providers/crm/hubspot.js +107 -85
- package/dist/providers/crm/hubspot.js.map +1 -1
- package/dist/providers/development/github.d.ts.map +1 -1
- package/dist/providers/development/github.js +40 -43
- package/dist/providers/development/github.js.map +1 -1
- package/dist/providers/ecommerce/shopify.d.ts.map +1 -1
- package/dist/providers/ecommerce/shopify.js +79 -62
- package/dist/providers/ecommerce/shopify.js.map +1 -1
- package/dist/providers/email/resend.d.ts.map +1 -1
- package/dist/providers/email/resend.js +20 -16
- package/dist/providers/email/resend.js.map +1 -1
- package/dist/providers/email/sendgrid.d.ts.map +1 -1
- package/dist/providers/email/sendgrid.js +12 -9
- package/dist/providers/email/sendgrid.js.map +1 -1
- package/dist/providers/finance/stripe.d.ts.map +1 -1
- package/dist/providers/finance/stripe.js +44 -42
- package/dist/providers/finance/stripe.js.map +1 -1
- package/dist/providers/forms/typeform.d.ts.map +1 -1
- package/dist/providers/forms/typeform.js +68 -58
- package/dist/providers/forms/typeform.js.map +1 -1
- package/dist/providers/knowledge/notion.d.ts.map +1 -1
- package/dist/providers/knowledge/notion.js +75 -41
- package/dist/providers/knowledge/notion.js.map +1 -1
- package/dist/providers/marketing/mailchimp.d.ts.map +1 -1
- package/dist/providers/marketing/mailchimp.js +74 -61
- package/dist/providers/marketing/mailchimp.js.map +1 -1
- package/dist/providers/media/cloudinary.d.ts.map +1 -1
- package/dist/providers/media/cloudinary.js +30 -28
- package/dist/providers/media/cloudinary.js.map +1 -1
- package/dist/providers/messaging/slack.d.ts.map +1 -1
- package/dist/providers/messaging/slack.js +75 -58
- package/dist/providers/messaging/slack.js.map +1 -1
- package/dist/providers/messaging/twilio-sms.d.ts.map +1 -1
- package/dist/providers/messaging/twilio-sms.js +33 -15
- package/dist/providers/messaging/twilio-sms.js.map +1 -1
- package/dist/providers/project-management/linear.d.ts.map +1 -1
- package/dist/providers/project-management/linear.js +31 -27
- package/dist/providers/project-management/linear.js.map +1 -1
- package/dist/providers/spreadsheet/google-sheets.d.ts.map +1 -1
- package/dist/providers/spreadsheet/google-sheets.js +21 -18
- package/dist/providers/spreadsheet/google-sheets.js.map +1 -1
- package/dist/providers/spreadsheet/xlsx.d.ts.map +1 -1
- package/dist/providers/spreadsheet/xlsx.js +4 -4
- package/dist/providers/spreadsheet/xlsx.js.map +1 -1
- package/dist/providers/storage/index.js +1 -0
- package/dist/providers/storage/index.js.map +1 -1
- package/dist/providers/storage/s3.d.ts.map +1 -1
- package/dist/providers/storage/s3.js +36 -27
- package/dist/providers/storage/s3.js.map +1 -1
- package/dist/providers/support/zendesk.d.ts.map +1 -1
- package/dist/providers/support/zendesk.js +24 -25
- package/dist/providers/support/zendesk.js.map +1 -1
- package/dist/providers/tasks/todoist.d.ts.map +1 -1
- package/dist/providers/tasks/todoist.js +18 -18
- package/dist/providers/tasks/todoist.js.map +1 -1
- package/dist/providers/video-conferencing/google-meet.d.ts.map +1 -1
- package/dist/providers/video-conferencing/google-meet.js +11 -11
- package/dist/providers/video-conferencing/google-meet.js.map +1 -1
- package/dist/providers/video-conferencing/jitsi.js +14 -14
- package/dist/providers/video-conferencing/jitsi.js.map +1 -1
- package/dist/providers/video-conferencing/teams.d.ts.map +1 -1
- package/dist/providers/video-conferencing/teams.js +9 -7
- package/dist/providers/video-conferencing/teams.js.map +1 -1
- package/dist/providers/video-conferencing/zoom.d.ts.map +1 -1
- package/dist/providers/video-conferencing/zoom.js +26 -24
- package/dist/providers/video-conferencing/zoom.js.map +1 -1
- package/dist/tools/data.d.ts.map +1 -1
- package/dist/tools/data.js +5 -12
- package/dist/tools/data.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/system.d.ts +289 -0
- package/dist/tools/system.d.ts.map +1 -0
- package/dist/tools/system.js +752 -0
- package/dist/tools/system.js.map +1 -0
- package/dist/tools/web.d.ts.map +1 -1
- package/dist/tools/web.js +22 -10
- package/dist/tools/web.js.map +1 -1
- package/dist/track-record.d.ts +101 -0
- package/dist/track-record.d.ts.map +1 -0
- package/dist/track-record.js +17 -0
- package/dist/track-record.js.map +1 -0
- package/dist/types.d.ts +210 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/verb-registration.d.ts +122 -0
- package/dist/verb-registration.d.ts.map +1 -0
- package/dist/verb-registration.js +176 -0
- package/dist/verb-registration.js.map +1 -0
- package/dist/worker.d.ts +93 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +315 -0
- package/dist/worker.js.map +1 -0
- package/dist/wrap.d.ts +89 -0
- package/dist/wrap.d.ts.map +1 -0
- package/dist/wrap.js +225 -0
- package/dist/wrap.js.map +1 -0
- package/package.json +21 -4
- package/src/client.ts +136 -0
- package/src/define.ts +31 -37
- package/src/function-ref.ts +264 -0
- package/src/function-sugar.ts +134 -0
- package/src/index.ts +132 -10
- package/src/providers/analytics/mixpanel.ts +19 -18
- package/src/providers/calendar/cal-com.ts +29 -18
- package/src/providers/calendar/google-calendar.ts +20 -14
- package/src/providers/crm/hubspot.ts +225 -99
- package/src/providers/development/github.ts +206 -135
- package/src/providers/ecommerce/shopify.ts +250 -89
- package/src/providers/email/resend.ts +101 -28
- package/src/providers/email/sendgrid.ts +12 -9
- package/src/providers/finance/stripe.ts +128 -49
- package/src/providers/forms/typeform.ts +74 -58
- package/src/providers/knowledge/notion.ts +340 -88
- package/src/providers/marketing/mailchimp.ts +86 -70
- package/src/providers/media/cloudinary.ts +99 -41
- package/src/providers/messaging/slack.ts +283 -85
- package/src/providers/messaging/twilio-sms.ts +35 -15
- package/src/providers/project-management/linear.ts +143 -55
- package/src/providers/spreadsheet/google-sheets.ts +222 -56
- package/src/providers/spreadsheet/xlsx.ts +47 -16
- package/src/providers/storage/s3.ts +119 -47
- package/src/providers/support/zendesk.ts +196 -46
- package/src/providers/tasks/todoist.ts +20 -26
- package/src/providers/video-conferencing/google-meet.ts +17 -20
- package/src/providers/video-conferencing/jitsi.ts +14 -14
- package/src/providers/video-conferencing/teams.ts +14 -13
- package/src/providers/video-conferencing/zoom.ts +54 -49
- package/src/tools/data.ts +6 -16
- package/src/tools/index.ts +1 -0
- package/src/tools/system.ts +887 -0
- package/src/tools/web.ts +22 -10
- package/src/track-record.ts +106 -0
- package/src/types.ts +241 -13
- package/src/verb-registration.ts +197 -0
- package/src/worker.ts +370 -0
- package/src/wrap.ts +260 -0
- package/test/client.test.ts +146 -0
- package/test/communication-tools-extended.test.ts +734 -0
- package/test/data-tools-extended.test.ts +743 -0
- package/test/define-extended.test.ts +819 -0
- package/test/define.test.ts +150 -41
- package/test/entities.test.ts +623 -0
- package/test/extended-entities.test.ts +1228 -0
- package/test/provider-implementations.test.ts +725 -0
- package/test/provider-registry-extended.test.ts +583 -0
- package/test/providers/google-sheets.test.ts +851 -0
- package/test/providers/helpers.ts +554 -0
- package/test/providers/hubspot.test.ts +576 -0
- package/test/providers/slack.test.ts +932 -0
- package/test/providers/stripe.test.ts +701 -0
- package/test/providers.test.ts +578 -0
- package/test/system-tools-extended.test.ts +632 -0
- package/test/system.test.ts +673 -0
- package/test/tools.test.ts +15 -11
- package/test/types.test.ts +402 -0
- package/test/verb-registration.test.ts +395 -0
- package/test/web-tools.test.ts +553 -0
- package/test/worker-extended.test.ts +699 -0
- package/test/worker.test.ts +576 -0
- package/test/wrap.test.ts +366 -0
- package/tsconfig.json +3 -13
- package/vitest.config.ts +37 -0
- package/wrangler.jsonc +9 -0
- package/.turbo/turbo-build.log +0 -5
- package/dist/providers/voice/vapi.d.ts +0 -27
- package/dist/providers/voice/vapi.d.ts.map +0 -1
- package/dist/providers/voice/vapi.js +0 -440
- package/dist/providers/voice/vapi.js.map +0 -1
- package/src/define.js +0 -267
- package/src/entities/advertising.js +0 -999
- package/src/entities/ai.js +0 -756
- package/src/entities/analytics.js +0 -1588
- package/src/entities/automation.js +0 -601
- package/src/entities/communication.js +0 -1150
- package/src/entities/crm.js +0 -1386
- package/src/entities/design.js +0 -546
- package/src/entities/development.js +0 -2212
- package/src/entities/document.js +0 -874
- package/src/entities/ecommerce.js +0 -1429
- package/src/entities/experiment.js +0 -1039
- package/src/entities/finance.js +0 -3478
- package/src/entities/forms.js +0 -1892
- package/src/entities/hr.js +0 -661
- package/src/entities/identity.js +0 -997
- package/src/entities/index.js +0 -282
- package/src/entities/infrastructure.js +0 -1153
- package/src/entities/knowledge.js +0 -1438
- package/src/entities/marketing.js +0 -1610
- package/src/entities/media.js +0 -1634
- package/src/entities/notification.js +0 -1199
- package/src/entities/presentation.js +0 -1274
- package/src/entities/productivity.js +0 -1317
- package/src/entities/project-management.js +0 -1136
- package/src/entities/recruiting.js +0 -736
- package/src/entities/shipping.js +0 -509
- package/src/entities/signature.js +0 -1102
- package/src/entities/site.js +0 -222
- package/src/entities/spreadsheet.js +0 -1341
- package/src/entities/storage.js +0 -1198
- package/src/entities/support.js +0 -1166
- package/src/entities/video-conferencing.js +0 -1750
- package/src/entities/video.js +0 -950
- package/src/entities.js +0 -1663
- package/src/index.js +0 -74
- package/src/providers/analytics/index.js +0 -17
- package/src/providers/analytics/mixpanel.js +0 -255
- package/src/providers/calendar/cal-com.js +0 -303
- package/src/providers/calendar/google-calendar.js +0 -335
- package/src/providers/calendar/index.js +0 -20
- package/src/providers/crm/hubspot.js +0 -566
- package/src/providers/crm/index.js +0 -17
- package/src/providers/development/github.js +0 -472
- package/src/providers/development/index.js +0 -17
- package/src/providers/ecommerce/index.js +0 -17
- package/src/providers/ecommerce/shopify.js +0 -378
- package/src/providers/email/index.js +0 -20
- package/src/providers/email/resend.js +0 -258
- package/src/providers/email/sendgrid.js +0 -161
- package/src/providers/finance/index.js +0 -17
- package/src/providers/finance/stripe.js +0 -549
- package/src/providers/forms/index.js +0 -17
- package/src/providers/forms/typeform.js +0 -500
- package/src/providers/index.js +0 -123
- package/src/providers/knowledge/index.js +0 -17
- package/src/providers/knowledge/notion.js +0 -389
- package/src/providers/marketing/index.js +0 -17
- package/src/providers/marketing/mailchimp.js +0 -443
- package/src/providers/media/cloudinary.js +0 -318
- package/src/providers/media/index.js +0 -17
- package/src/providers/messaging/index.js +0 -20
- package/src/providers/messaging/slack.js +0 -393
- package/src/providers/messaging/twilio-sms.js +0 -249
- package/src/providers/project-management/index.js +0 -17
- package/src/providers/project-management/linear.js +0 -575
- package/src/providers/registry.js +0 -86
- package/src/providers/spreadsheet/google-sheets.js +0 -375
- package/src/providers/spreadsheet/index.js +0 -20
- package/src/providers/spreadsheet/xlsx.js +0 -423
- package/src/providers/storage/index.js +0 -24
- package/src/providers/storage/s3.js +0 -419
- package/src/providers/support/index.js +0 -17
- package/src/providers/support/zendesk.js +0 -373
- package/src/providers/tasks/index.js +0 -17
- package/src/providers/tasks/todoist.js +0 -286
- package/src/providers/types.js +0 -9
- package/src/providers/video-conferencing/google-meet.js +0 -286
- package/src/providers/video-conferencing/index.js +0 -31
- package/src/providers/video-conferencing/jitsi.js +0 -254
- package/src/providers/video-conferencing/teams.js +0 -270
- package/src/providers/video-conferencing/zoom.js +0 -332
- package/src/registry.js +0 -128
- package/src/tools/communication.js +0 -184
- package/src/tools/data.js +0 -205
- package/src/tools/index.js +0 -11
- package/src/tools/web.js +0 -137
- package/src/types.js +0 -10
- package/test/define.test.js +0 -306
- package/test/registry.test.js +0 -357
- package/test/tools.test.js +0 -363
package/src/tools/web.ts
CHANGED
|
@@ -23,7 +23,11 @@ export const fetchUrl = defineTool<
|
|
|
23
23
|
type: 'object',
|
|
24
24
|
properties: {
|
|
25
25
|
url: { type: 'string', description: 'URL to fetch' },
|
|
26
|
-
method: {
|
|
26
|
+
method: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
description: 'HTTP method (default: GET)',
|
|
29
|
+
enum: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
|
|
30
|
+
},
|
|
27
31
|
headers: { type: 'object', description: 'Request headers' },
|
|
28
32
|
body: { type: 'string', description: 'Request body (for POST/PUT/PATCH)' },
|
|
29
33
|
},
|
|
@@ -32,8 +36,8 @@ export const fetchUrl = defineTool<
|
|
|
32
36
|
handler: async (input) => {
|
|
33
37
|
const response = await fetch(input.url, {
|
|
34
38
|
method: input.method || 'GET',
|
|
35
|
-
headers: input.headers,
|
|
36
|
-
body: input.body,
|
|
39
|
+
...(input.headers !== undefined && { headers: input.headers }),
|
|
40
|
+
...(input.body !== undefined && { body: input.body }),
|
|
37
41
|
})
|
|
38
42
|
|
|
39
43
|
const headers: Record<string, string> = {}
|
|
@@ -75,21 +79,26 @@ export const parseHtml = defineTool<
|
|
|
75
79
|
},
|
|
76
80
|
handler: async (input) => {
|
|
77
81
|
// Basic HTML parsing without DOM (would use cheerio or jsdom in production)
|
|
78
|
-
const text = input.html
|
|
82
|
+
const text = input.html
|
|
83
|
+
.replace(/<[^>]*>/g, ' ')
|
|
84
|
+
.replace(/\s+/g, ' ')
|
|
85
|
+
.trim()
|
|
79
86
|
|
|
80
87
|
// Extract links
|
|
81
88
|
const linkRegex = /href="([^"]+)"/g
|
|
82
89
|
const links: string[] = []
|
|
83
90
|
let match
|
|
84
91
|
while ((match = linkRegex.exec(input.html)) !== null) {
|
|
85
|
-
|
|
92
|
+
if (match[1] !== undefined) {
|
|
93
|
+
links.push(match[1])
|
|
94
|
+
}
|
|
86
95
|
}
|
|
87
96
|
|
|
88
97
|
// Extract images
|
|
89
98
|
const imgRegex = /src="([^"]+)"/g
|
|
90
99
|
const images: string[] = []
|
|
91
100
|
while ((match = imgRegex.exec(input.html)) !== null) {
|
|
92
|
-
if (match[1].match(/\.(jpg|jpeg|png|gif|webp|svg)/i)) {
|
|
101
|
+
if (match[1] !== undefined && match[1].match(/\.(jpg|jpeg|png|gif|webp|svg)/i)) {
|
|
93
102
|
images.push(match[1])
|
|
94
103
|
}
|
|
95
104
|
}
|
|
@@ -127,12 +136,13 @@ export const readUrl = defineTool<
|
|
|
127
136
|
|
|
128
137
|
// Extract title
|
|
129
138
|
const titleMatch = html.match(/<title[^>]*>([^<]+)<\/title>/i)
|
|
130
|
-
const title = titleMatch ? titleMatch[1].trim() : ''
|
|
139
|
+
const title = titleMatch && titleMatch[1] ? titleMatch[1].trim() : ''
|
|
131
140
|
|
|
132
141
|
// Extract body text
|
|
133
142
|
const bodyMatch = html.match(/<body[^>]*>([\s\S]*)<\/body>/i)
|
|
134
|
-
const body = bodyMatch ? bodyMatch[1] : html
|
|
135
|
-
const text = body
|
|
143
|
+
const body = bodyMatch && bodyMatch[1] ? bodyMatch[1] : html
|
|
144
|
+
const text = body
|
|
145
|
+
.replace(/<script[\s\S]*?<\/script>/gi, '')
|
|
136
146
|
.replace(/<style[\s\S]*?<\/style>/gi, '')
|
|
137
147
|
.replace(/<[^>]*>/g, ' ')
|
|
138
148
|
.replace(/\s+/g, ' ')
|
|
@@ -143,7 +153,9 @@ export const readUrl = defineTool<
|
|
|
143
153
|
const links: string[] = []
|
|
144
154
|
let match
|
|
145
155
|
while ((match = linkRegex.exec(html)) !== null) {
|
|
146
|
-
|
|
156
|
+
if (match[1] !== undefined) {
|
|
157
|
+
links.push(match[1])
|
|
158
|
+
}
|
|
147
159
|
}
|
|
148
160
|
|
|
149
161
|
return { title, text: text.slice(0, 10000), links: [...new Set(links)] }
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Track Record + Agent Mode + Promotion Policy (v3 §6 — Function-as-typed-primitive)
|
|
3
|
+
*
|
|
4
|
+
* Earned-autonomy primitives shared across `digital-tools` Function refs and
|
|
5
|
+
* (later) `digital-workers`. A Function or Worker accumulates a {@link TrackRecord}
|
|
6
|
+
* over time; a {@link PromotionPolicy} declares the threshold rules that move a
|
|
7
|
+
* Function up or down the {@link AgentMode} ladder (manual → supervised →
|
|
8
|
+
* autonomous, and back).
|
|
9
|
+
*
|
|
10
|
+
* These types live in `digital-tools` because the Function discriminated union
|
|
11
|
+
* is the first consumer; `digital-workers` re-exports the same types when it
|
|
12
|
+
* lands so per-Worker autonomy and per-Function autonomy use one vocabulary.
|
|
13
|
+
*
|
|
14
|
+
* @packageDocumentation
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { Money } from 'autonomous-finance'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Operating mode of an agentic Function (or Worker).
|
|
21
|
+
*
|
|
22
|
+
* - `manual` — every invocation requires a human to initiate.
|
|
23
|
+
* - `supervised` — runs autonomously but every output is reviewed before
|
|
24
|
+
* being released downstream (gated on human sign-off).
|
|
25
|
+
* - `autonomous` — runs and releases without per-invocation review;
|
|
26
|
+
* oversight is post-hoc and sample-based.
|
|
27
|
+
*/
|
|
28
|
+
export type AgentMode = 'manual' | 'supervised' | 'autonomous'
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Direction of the most recent measured trend in a {@link TrackRecord}.
|
|
32
|
+
*/
|
|
33
|
+
export type TrendDirection = 'improving' | 'stable' | 'declining'
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Quality + cost + oversight signal accumulated over a Function or Worker's
|
|
37
|
+
* recent invocations. Drives {@link PromotionPolicy} thresholds.
|
|
38
|
+
*
|
|
39
|
+
* All fields are present for any Function that has been invoked at least once;
|
|
40
|
+
* the registry materialises a zero-sample record on first registration so
|
|
41
|
+
* thresholds always have a value to compare against.
|
|
42
|
+
*/
|
|
43
|
+
export interface TrackRecord {
|
|
44
|
+
/** Fraction of invocations that met the OutcomeContract (0–1). */
|
|
45
|
+
accuracy: number
|
|
46
|
+
/** Number of invocations the record summarises. */
|
|
47
|
+
samples: number
|
|
48
|
+
/** Direction of the most recent measured trend. */
|
|
49
|
+
trend: TrendDirection
|
|
50
|
+
/** ISO-8601 timestamp of the most recent sample. */
|
|
51
|
+
lastUpdated: string
|
|
52
|
+
/** Average cost per successful invocation; absent until at least one success. */
|
|
53
|
+
costPerSuccess?: Money
|
|
54
|
+
/**
|
|
55
|
+
* Fraction of supervised invocations whose human reviewer overrode the
|
|
56
|
+
* Function's output (0–1). High values indicate the Function is not ready
|
|
57
|
+
* for promotion to `autonomous`.
|
|
58
|
+
*/
|
|
59
|
+
reviewerOverrideRate?: number
|
|
60
|
+
/**
|
|
61
|
+
* Composite 0–100 score combining accuracy / cost / override rate. Used by
|
|
62
|
+
* downstream catalog UI when ranking Functions; the precise formula lives
|
|
63
|
+
* in `ai-evaluate` so it can evolve without a type change here.
|
|
64
|
+
*/
|
|
65
|
+
digitalScore?: number
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Threshold-gated rule that promotes or demotes a Function between
|
|
70
|
+
* {@link AgentMode} values based on its accumulated {@link TrackRecord}.
|
|
71
|
+
*
|
|
72
|
+
* Predicates are expressed as strings so they can be persisted alongside the
|
|
73
|
+
* Function definition and re-evaluated against fresh records without a code
|
|
74
|
+
* change. The evaluator (lives in `ai-evaluate`) parses them against a
|
|
75
|
+
* `TrackRecord` plus invocation context.
|
|
76
|
+
*/
|
|
77
|
+
export interface PromotionRule {
|
|
78
|
+
/**
|
|
79
|
+
* String predicate evaluated against the {@link TrackRecord} (and optionally
|
|
80
|
+
* elapsed time / sample size). Examples:
|
|
81
|
+
* - `'accuracy >= 0.95 && samples >= 100'`
|
|
82
|
+
* - `'reviewerOverrideRate < 0.05 && trend === "improving"'`
|
|
83
|
+
*/
|
|
84
|
+
when: string
|
|
85
|
+
/** Mode to move the Function to when {@link when} holds. */
|
|
86
|
+
to: AgentMode
|
|
87
|
+
/** Optional human-readable description of the rule's intent. */
|
|
88
|
+
rationale?: string
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Declarative ladder describing when a Function may climb to a more
|
|
93
|
+
* autonomous {@link AgentMode} and when it must step back down.
|
|
94
|
+
*
|
|
95
|
+
* `evaluate` sets the cadence at which the policy is re-checked against the
|
|
96
|
+
* latest {@link TrackRecord}; `'continuous'` re-evaluates after every
|
|
97
|
+
* invocation, `'daily'` / `'weekly'` use a scheduled job.
|
|
98
|
+
*/
|
|
99
|
+
export interface PromotionPolicy {
|
|
100
|
+
/** Rules that move the Function up the autonomy ladder. */
|
|
101
|
+
promote: PromotionRule[]
|
|
102
|
+
/** Rules that move the Function down the autonomy ladder. */
|
|
103
|
+
demote: PromotionRule[]
|
|
104
|
+
/** Cadence at which the policy is re-evaluated. */
|
|
105
|
+
evaluate: 'continuous' | 'daily' | 'weekly'
|
|
106
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -9,8 +9,17 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { JSONSchema } from 'ai-functions'
|
|
12
|
+
import type { Frame, ActionRef } from 'digital-objects'
|
|
13
|
+
import type { Identity, PaymentReceipt } from 'id.org.ai'
|
|
12
14
|
import type { z } from 'zod'
|
|
13
15
|
|
|
16
|
+
// Re-export for downstream consumers (digital-tasks/digital-workers can use the same Frame)
|
|
17
|
+
export type { Frame, ActionRef } from 'digital-objects'
|
|
18
|
+
|
|
19
|
+
// Re-export id.org.ai canonical types so consumers don't need a separate import.
|
|
20
|
+
// `wrapTool()` populates these on `ToolHandlerContext` after broker gating.
|
|
21
|
+
export type { Identity, PaymentReceipt } from 'id.org.ai'
|
|
22
|
+
|
|
14
23
|
// ============================================================================
|
|
15
24
|
// Tool Category Ontology
|
|
16
25
|
// ============================================================================
|
|
@@ -38,10 +47,10 @@ export type ToolCategory =
|
|
|
38
47
|
export type CommunicationSubcategory =
|
|
39
48
|
| 'email'
|
|
40
49
|
| 'sms'
|
|
41
|
-
| 'channel'
|
|
42
|
-
| 'workspace'
|
|
50
|
+
| 'channel' // Brand-agnostic team messaging (Slack/Teams/Discord)
|
|
51
|
+
| 'workspace' // Team messaging workspace/organization
|
|
43
52
|
| 'direct-message' // Private/DM conversations
|
|
44
|
-
| 'chat'
|
|
53
|
+
| 'chat' // Generic chat
|
|
45
54
|
| 'notification'
|
|
46
55
|
| 'voice'
|
|
47
56
|
| 'video-call'
|
|
@@ -283,15 +292,166 @@ export interface ToolOutput {
|
|
|
283
292
|
streaming?: boolean
|
|
284
293
|
}
|
|
285
294
|
|
|
295
|
+
// ============================================================================
|
|
296
|
+
// SVO Co-Design Types (aip-oejp): verb / frame / auth / pricing / handler ctx
|
|
297
|
+
// ============================================================================
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Reference to an Identity in `id.org.ai`.
|
|
301
|
+
*
|
|
302
|
+
* Currently a string ID; the canonical type will move to `id.org.ai`
|
|
303
|
+
* once that package is published. Until then, treat as opaque.
|
|
304
|
+
*/
|
|
305
|
+
export type IdentityRef = string
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Payment rail used to satisfy a tool's `pricing` requirement.
|
|
309
|
+
*
|
|
310
|
+
* Minimal local definition — the canonical type will come from
|
|
311
|
+
* `id.org.ai` (PaymentBroker) once that package is published.
|
|
312
|
+
*/
|
|
313
|
+
export interface PaymentRail {
|
|
314
|
+
/** Rail used to settle the payment */
|
|
315
|
+
rail: 'x402' | 'mpp'
|
|
316
|
+
/** Optional receipt / proof of payment from the broker */
|
|
317
|
+
receipt?: string
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Authentication requirement for invoking a tool.
|
|
322
|
+
*
|
|
323
|
+
* Declares which auth scheme and scopes the caller must present
|
|
324
|
+
* before the handler is executed.
|
|
325
|
+
*/
|
|
326
|
+
export interface AuthRequirement {
|
|
327
|
+
/** OAuth scopes / API key scopes required */
|
|
328
|
+
scopes: string[]
|
|
329
|
+
/** Auth mechanism the tool requires; `none` means public */
|
|
330
|
+
required: 'oauth' | 'apiKey' | 'none'
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Payment requirement attached to a tool, MDXLD-shaped and
|
|
335
|
+
* compatible with x402 / MPP payment rails.
|
|
336
|
+
*
|
|
337
|
+
* When present, callers must satisfy this before the handler runs;
|
|
338
|
+
* the broker (id.org.ai) negotiates an acceptable rail and injects
|
|
339
|
+
* a `PaymentRail` into the handler context.
|
|
340
|
+
*/
|
|
341
|
+
export interface PaymentRequired {
|
|
342
|
+
/** MDXLD type discriminator */
|
|
343
|
+
$type: 'PaymentRequired'
|
|
344
|
+
/** Amount as a string (preserves precision for crypto/fiat) */
|
|
345
|
+
amount: string
|
|
346
|
+
/** ISO 4217 currency code or asset symbol (e.g. 'USD', 'USDC') */
|
|
347
|
+
currency: string
|
|
348
|
+
/** Payment rails this tool will accept */
|
|
349
|
+
accepts: ('x402' | 'mpp')[]
|
|
350
|
+
/** Wallet address or stripeAccountId of the recipient */
|
|
351
|
+
recipient: string
|
|
352
|
+
/** Optional facilitator URL/identifier */
|
|
353
|
+
facilitator?: string
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Context object passed to a tool handler when invoked through
|
|
358
|
+
* an SVO-aware dispatcher.
|
|
359
|
+
*
|
|
360
|
+
* This is distinct from the registry-level `ToolContext` (executor
|
|
361
|
+
* metadata for tracing/audit) — `ToolHandlerContext` carries
|
|
362
|
+
* runtime resources the handler needs to perform the action:
|
|
363
|
+
* the caller's identity, an injected payment rail (if `pricing`
|
|
364
|
+
* was satisfied), and the parent Action that caused the call.
|
|
365
|
+
*
|
|
366
|
+
* Handlers that don't need any of this can keep the arity-1
|
|
367
|
+
* `(args) => result` signature; arity-2 `(args, ctx) => result`
|
|
368
|
+
* is opt-in.
|
|
369
|
+
*
|
|
370
|
+
* `wrapTool()` (the broker-aware HTTP wrapper) populates `identity`
|
|
371
|
+
* with a full id.org.ai `Identity` record and, when `pricing` is
|
|
372
|
+
* satisfied, populates `paymentReceipt` with the canonical receipt
|
|
373
|
+
* from `PaymentBroker.settle()`. Older dispatchers that pass an
|
|
374
|
+
* opaque `IdentityRef` string and/or the local `PaymentRail` shape
|
|
375
|
+
* remain supported (the handler signature widens, not narrows).
|
|
376
|
+
*/
|
|
377
|
+
export interface ToolHandlerContext {
|
|
378
|
+
/**
|
|
379
|
+
* Identity of the caller. Either:
|
|
380
|
+
* - the canonical {@link Identity} record from `id.org.ai`
|
|
381
|
+
* (populated by `wrapTool()` after `AuthBroker.gate()`), or
|
|
382
|
+
* - an opaque {@link IdentityRef} string for legacy dispatchers.
|
|
383
|
+
*/
|
|
384
|
+
identity: Identity | IdentityRef
|
|
385
|
+
/**
|
|
386
|
+
* Backwards-compatible local `PaymentRail` shape. Older dispatchers
|
|
387
|
+
* may set this; new code should prefer {@link paymentReceipt} which
|
|
388
|
+
* carries the full id.org.ai receipt (txRef, settledAt, response
|
|
389
|
+
* header, …).
|
|
390
|
+
*/
|
|
391
|
+
payment?: PaymentRail
|
|
392
|
+
/**
|
|
393
|
+
* Canonical {@link PaymentReceipt} from `PaymentBroker.settle()`.
|
|
394
|
+
* Populated by `wrapTool()` when the tool's `pricing` is satisfied.
|
|
395
|
+
*/
|
|
396
|
+
paymentReceipt?: PaymentReceipt
|
|
397
|
+
/** Parent Action that caused this tool invocation, if any */
|
|
398
|
+
parentAction?: ActionRef
|
|
399
|
+
}
|
|
400
|
+
|
|
286
401
|
// ============================================================================
|
|
287
402
|
// Core Tool Interface
|
|
288
403
|
// ============================================================================
|
|
289
404
|
|
|
290
405
|
/**
|
|
291
|
-
* Core Tool definition - the foundational type
|
|
406
|
+
* Core Tool definition - the foundational type for digital tools.
|
|
407
|
+
*
|
|
408
|
+
* Tools can be used by both humans and AI agents. They provide a standardized
|
|
409
|
+
* interface for performing actions across various categories including
|
|
410
|
+
* communication, data, development, documents, finance, and more.
|
|
411
|
+
*
|
|
412
|
+
* @typeParam TInput - The type of input the tool handler accepts
|
|
413
|
+
* @typeParam TOutput - The type of output the tool handler returns
|
|
414
|
+
*
|
|
415
|
+
* @example Basic tool definition
|
|
416
|
+
* ```typescript
|
|
417
|
+
* import { Tool, defineTool } from 'digital-tools'
|
|
292
418
|
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
419
|
+
* const greetTool: Tool<{ name: string }, string> = {
|
|
420
|
+
* id: 'communication.greeting.hello',
|
|
421
|
+
* name: 'Greet User',
|
|
422
|
+
* description: 'Generates a greeting message for the specified user',
|
|
423
|
+
* category: 'communication',
|
|
424
|
+
* parameters: [{
|
|
425
|
+
* name: 'name',
|
|
426
|
+
* description: 'Name of the person to greet',
|
|
427
|
+
* schema: { type: 'string' },
|
|
428
|
+
* required: true,
|
|
429
|
+
* }],
|
|
430
|
+
* handler: ({ name }) => `Hello, ${name}!`,
|
|
431
|
+
* }
|
|
432
|
+
* ```
|
|
433
|
+
*
|
|
434
|
+
* @example Using defineTool helper with Zod schema
|
|
435
|
+
* ```typescript
|
|
436
|
+
* import { defineTool } from 'digital-tools'
|
|
437
|
+
* import { z } from 'zod'
|
|
438
|
+
*
|
|
439
|
+
* const fetchTool = defineTool({
|
|
440
|
+
* id: 'web.fetch.url',
|
|
441
|
+
* name: 'Fetch URL',
|
|
442
|
+
* description: 'Fetches content from a URL',
|
|
443
|
+
* category: 'web',
|
|
444
|
+
* input: z.object({ url: z.string().url() }),
|
|
445
|
+
* handler: async ({ url }) => {
|
|
446
|
+
* const response = await fetch(url)
|
|
447
|
+
* return response.text()
|
|
448
|
+
* },
|
|
449
|
+
* })
|
|
450
|
+
* ```
|
|
451
|
+
*
|
|
452
|
+
* @see {@link ToolCategory} for available categories
|
|
453
|
+
* @see {@link ToolRegistry} for tool registration and discovery
|
|
454
|
+
* @see {@link defineTool} for the recommended way to create tools
|
|
295
455
|
*/
|
|
296
456
|
export interface Tool<TInput = unknown, TOutput = unknown> {
|
|
297
457
|
/** Unique tool identifier (e.g., 'communication.email.send') */
|
|
@@ -329,6 +489,31 @@ export interface Tool<TInput = unknown, TOutput = unknown> {
|
|
|
329
489
|
/** Rate limiting */
|
|
330
490
|
rateLimit?: RateLimit
|
|
331
491
|
|
|
492
|
+
// SVO Co-Design (aip-oejp) — all optional, additive over existing tools
|
|
493
|
+
/**
|
|
494
|
+
* Canonical Verb name this tool implements (e.g. 'send', 'transcribe').
|
|
495
|
+
*
|
|
496
|
+
* Used by SVO-aware dispatchers to resolve a Verb in `digital-objects`
|
|
497
|
+
* and pick a Tool registered for it. Cross-package Verb auto-registration
|
|
498
|
+
* is intentionally NOT performed here — see follow-up bead.
|
|
499
|
+
*/
|
|
500
|
+
verb?: string
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Frame declaring the complement-role types this tool accepts.
|
|
504
|
+
*
|
|
505
|
+
* Reuses the canonical {@link Frame} type from `digital-objects` so
|
|
506
|
+
* Tool frames and Verb frames stay in lockstep. When absent, the tool
|
|
507
|
+
* is treated as permissive (frame inferred from input shape).
|
|
508
|
+
*/
|
|
509
|
+
frame?: Frame
|
|
510
|
+
|
|
511
|
+
/** Auth requirement gate; absent means no auth required */
|
|
512
|
+
auth?: AuthRequirement
|
|
513
|
+
|
|
514
|
+
/** Pricing requirement (x402/MPP); absent means free */
|
|
515
|
+
pricing?: PaymentRequired
|
|
516
|
+
|
|
332
517
|
// Input/Output
|
|
333
518
|
/** Input parameters */
|
|
334
519
|
parameters: ToolParameter[]
|
|
@@ -336,8 +521,16 @@ export interface Tool<TInput = unknown, TOutput = unknown> {
|
|
|
336
521
|
/** Output definition */
|
|
337
522
|
output?: ToolOutput
|
|
338
523
|
|
|
339
|
-
/**
|
|
340
|
-
|
|
524
|
+
/**
|
|
525
|
+
* The tool implementation.
|
|
526
|
+
*
|
|
527
|
+
* Backward-compatible: handlers may take just `(input)` (arity-1) or
|
|
528
|
+
* opt into the SVO handler context with `(input, ctx)` (arity-2).
|
|
529
|
+
* Dispatchers that don't have a {@link ToolHandlerContext} should
|
|
530
|
+
* call the handler with the input only — TypeScript permits this
|
|
531
|
+
* because the second parameter is optional.
|
|
532
|
+
*/
|
|
533
|
+
handler: (input: TInput, ctx?: ToolHandlerContext) => TOutput | Promise<TOutput>
|
|
341
534
|
|
|
342
535
|
// Metadata
|
|
343
536
|
/** Tool author/owner */
|
|
@@ -360,8 +553,22 @@ export interface Tool<TInput = unknown, TOutput = unknown> {
|
|
|
360
553
|
}
|
|
361
554
|
|
|
362
555
|
/**
|
|
363
|
-
* Any tool type - used for arrays and collections of tools
|
|
364
|
-
*
|
|
556
|
+
* Any tool type - used for arrays and collections of tools with different
|
|
557
|
+
* input/output types.
|
|
558
|
+
*
|
|
559
|
+
* Use this type when you need to work with heterogeneous collections of tools,
|
|
560
|
+
* such as in registries, tool lists, or when the specific input/output types
|
|
561
|
+
* are not known at compile time.
|
|
562
|
+
*
|
|
563
|
+
* @example Tool collection
|
|
564
|
+
* ```typescript
|
|
565
|
+
* import { AnyTool } from 'digital-tools'
|
|
566
|
+
*
|
|
567
|
+
* const tools: AnyTool[] = [emailTool, slackTool, fetchTool]
|
|
568
|
+
* tools.forEach(tool => console.log(tool.name))
|
|
569
|
+
* ```
|
|
570
|
+
*
|
|
571
|
+
* @see {@link Tool} for the base tool type with specific type parameters
|
|
365
572
|
*/
|
|
366
573
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
367
574
|
export type AnyTool = Tool<any, any>
|
|
@@ -388,10 +595,31 @@ export interface DefineToolOptions<TInput, TOutput> {
|
|
|
388
595
|
input: Schema
|
|
389
596
|
/** Output schema (optional) */
|
|
390
597
|
output?: Schema
|
|
391
|
-
|
|
392
|
-
|
|
598
|
+
|
|
599
|
+
// SVO Co-Design (aip-oejp) — all optional, additive
|
|
600
|
+
/** Canonical Verb name this tool implements */
|
|
601
|
+
verb?: string
|
|
602
|
+
/** Frame from `digital-objects` declaring complement-role types */
|
|
603
|
+
frame?: Frame
|
|
604
|
+
/** Auth requirement; absent means no auth required */
|
|
605
|
+
auth?: AuthRequirement
|
|
606
|
+
/** Pricing requirement (x402/MPP); absent means free */
|
|
607
|
+
pricing?: PaymentRequired
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* The handler function.
|
|
611
|
+
*
|
|
612
|
+
* Backward-compatible: arity-1 `(input)` handlers continue to work;
|
|
613
|
+
* arity-2 `(input, ctx)` handlers receive the SVO {@link ToolHandlerContext}.
|
|
614
|
+
*/
|
|
615
|
+
handler: (input: TInput, ctx?: ToolHandlerContext) => TOutput | Promise<TOutput>
|
|
393
616
|
/** Additional options */
|
|
394
|
-
options?: Partial<
|
|
617
|
+
options?: Partial<
|
|
618
|
+
Omit<
|
|
619
|
+
Tool<TInput, TOutput>,
|
|
620
|
+
'id' | 'name' | 'description' | 'category' | 'parameters' | 'handler'
|
|
621
|
+
>
|
|
622
|
+
>
|
|
395
623
|
}
|
|
396
624
|
|
|
397
625
|
/**
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verb Auto-Registration (aip-47tm)
|
|
3
|
+
*
|
|
4
|
+
* Bridges `defineTool()` (synchronous, side-effect-free builder) to
|
|
5
|
+
* `digital-objects`' provider-mediated `defineVerb()` (async, I/O-bound).
|
|
6
|
+
*
|
|
7
|
+
* ## Design choice — Option B: explicit bootstrap
|
|
8
|
+
*
|
|
9
|
+
* `defineTool({ verb, frame })` records the metadata on the Tool but does
|
|
10
|
+
* NOT call `provider.defineVerb()` itself. Cross-package side effects
|
|
11
|
+
* from a pure builder are surprising; the provider is also caller-supplied,
|
|
12
|
+
* so eager registration would force `defineTool` to know about a provider
|
|
13
|
+
* it doesn't currently take.
|
|
14
|
+
*
|
|
15
|
+
* Instead, registration is an explicit, idempotent helper:
|
|
16
|
+
*
|
|
17
|
+
* - `registerToolVerb(provider, tool)` — single-tool, used from
|
|
18
|
+
* custom dispatchers that
|
|
19
|
+
* want lazy-on-first-call.
|
|
20
|
+
* - `registerToolVerbs(provider, tools)` — bootstrap list, used at
|
|
21
|
+
* startup once per provider.
|
|
22
|
+
*
|
|
23
|
+
* Both are idempotent: re-registering a Verb with the same name and same
|
|
24
|
+
* frame is a no-op. Re-registering with a different frame throws —
|
|
25
|
+
* silent overwrite would mask an ontology drift bug.
|
|
26
|
+
*
|
|
27
|
+
* ## Why not Option A (lazy in `wrapTool`)
|
|
28
|
+
*
|
|
29
|
+
* `wrapTool()` takes auth + payment brokers, not a `DigitalObjectsProvider`.
|
|
30
|
+
* Wiring a provider into `wrapTool()` would couple HTTP wrapping to the
|
|
31
|
+
* digital-objects ontology — a layering inversion. Callers that want
|
|
32
|
+
* lazy-on-first-call can compose `registerToolVerb()` into their own
|
|
33
|
+
* dispatcher; the helper is fast enough to call per-request because the
|
|
34
|
+
* idempotency check short-circuits to a single `getVerb()` lookup.
|
|
35
|
+
*
|
|
36
|
+
* ## Why not Option C (eager-with-deferred-provider queue)
|
|
37
|
+
*
|
|
38
|
+
* A module-level queue is global state in `digital-tools` — exactly what
|
|
39
|
+
* the bead forbids. It also can't flush deterministically across
|
|
40
|
+
* concurrent test workers, and ordering becomes load-dependent.
|
|
41
|
+
*
|
|
42
|
+
* @packageDocumentation
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
import type { Frame, Verb, VerbDefinition } from 'digital-objects'
|
|
46
|
+
import type { AnyTool, Tool } from './types.js'
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Subset of `DigitalObjectsProvider` that this module requires.
|
|
50
|
+
*
|
|
51
|
+
* We deliberately accept only `getVerb` + `defineVerb` (rather than the
|
|
52
|
+
* full provider interface) so callers can pass any object with these
|
|
53
|
+
* two methods — including HTTP/RPC clients, test doubles, and the
|
|
54
|
+
* canonical `MemoryProvider`.
|
|
55
|
+
*/
|
|
56
|
+
export interface VerbRegistrationProvider {
|
|
57
|
+
defineVerb(def: VerbDefinition): Promise<Verb>
|
|
58
|
+
getVerb(name: string): Promise<Verb | null>
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Compare two optional Frames structurally. Returns true when both are
|
|
63
|
+
* undefined, or both define the same set of role -> NounRef mappings.
|
|
64
|
+
*
|
|
65
|
+
* `manner` (string[]) is compared as an unordered set since the role
|
|
66
|
+
* order is not semantically meaningful.
|
|
67
|
+
*/
|
|
68
|
+
function framesEqual(a: Frame | undefined, b: Frame | undefined): boolean {
|
|
69
|
+
if (a === b) return true
|
|
70
|
+
if (!a || !b) return false
|
|
71
|
+
|
|
72
|
+
const keys = new Set<keyof Frame>([
|
|
73
|
+
...(Object.keys(a) as (keyof Frame)[]),
|
|
74
|
+
...(Object.keys(b) as (keyof Frame)[]),
|
|
75
|
+
])
|
|
76
|
+
|
|
77
|
+
for (const k of keys) {
|
|
78
|
+
const av = a[k]
|
|
79
|
+
const bv = b[k]
|
|
80
|
+
if (k === 'manner') {
|
|
81
|
+
const aArr = (av as string[] | undefined) ?? []
|
|
82
|
+
const bArr = (bv as string[] | undefined) ?? []
|
|
83
|
+
if (aArr.length !== bArr.length) return false
|
|
84
|
+
const aSet = new Set(aArr)
|
|
85
|
+
for (const v of bArr) if (!aSet.has(v)) return false
|
|
86
|
+
} else {
|
|
87
|
+
if (av !== bv) return false
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return true
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Mismatch error raised when a Tool tries to register a Verb whose name
|
|
95
|
+
* already exists with a different frame. We do not silently overwrite —
|
|
96
|
+
* conflicting ontology shapes are almost always a bug (two tools claiming
|
|
97
|
+
* the same verb with different role expectations).
|
|
98
|
+
*/
|
|
99
|
+
export class VerbRegistrationConflictError extends Error {
|
|
100
|
+
constructor(
|
|
101
|
+
public readonly verbName: string,
|
|
102
|
+
public readonly existing: Frame | undefined,
|
|
103
|
+
public readonly incoming: Frame | undefined
|
|
104
|
+
) {
|
|
105
|
+
super(
|
|
106
|
+
`Verb "${verbName}" is already registered with a different frame. ` +
|
|
107
|
+
`Tools that share a verb name must declare the same frame. ` +
|
|
108
|
+
`Re-registering a verb with the same frame is a no-op (idempotent), ` +
|
|
109
|
+
`but the incoming frame does not match the existing one.`
|
|
110
|
+
)
|
|
111
|
+
this.name = 'VerbRegistrationConflictError'
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Register the Verb declared by a single Tool against `provider`.
|
|
117
|
+
*
|
|
118
|
+
* - Tools without a `verb` field are skipped (they are not SVO-aware).
|
|
119
|
+
* - If the verb already exists with the same frame, the call is a no-op
|
|
120
|
+
* and the existing Verb is returned (idempotent).
|
|
121
|
+
* - If the verb already exists with a different frame, throws
|
|
122
|
+
* {@link VerbRegistrationConflictError}.
|
|
123
|
+
*
|
|
124
|
+
* Suitable for both eager bootstrap and lazy-per-request use:
|
|
125
|
+
* the idempotency check is a single `getVerb()` lookup, so it is safe
|
|
126
|
+
* to call from a hot dispatch path.
|
|
127
|
+
*
|
|
128
|
+
* @returns The registered Verb, or `null` if the tool has no `verb` field.
|
|
129
|
+
*
|
|
130
|
+
* @example Lazy registration in a custom dispatcher
|
|
131
|
+
* ```ts
|
|
132
|
+
* async function dispatch(provider: DigitalObjectsProvider, tool: Tool, args: unknown) {
|
|
133
|
+
* await registerToolVerb(provider, tool) // idempotent, cheap on subsequent calls
|
|
134
|
+
* return tool.handler(args)
|
|
135
|
+
* }
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
export async function registerToolVerb(
|
|
139
|
+
provider: VerbRegistrationProvider,
|
|
140
|
+
tool: Tool<unknown, unknown> | AnyTool
|
|
141
|
+
): Promise<Verb | null> {
|
|
142
|
+
if (!tool.verb) return null
|
|
143
|
+
|
|
144
|
+
const existing = await provider.getVerb(tool.verb)
|
|
145
|
+
if (existing) {
|
|
146
|
+
if (framesEqual(existing.frame, tool.frame)) {
|
|
147
|
+
return existing
|
|
148
|
+
}
|
|
149
|
+
throw new VerbRegistrationConflictError(tool.verb, existing.frame, tool.frame)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return provider.defineVerb({
|
|
153
|
+
name: tool.verb,
|
|
154
|
+
...(tool.frame !== undefined && { frame: tool.frame }),
|
|
155
|
+
...(tool.description !== undefined && { description: tool.description }),
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Bootstrap helper: register the Verbs for every SVO-aware tool in
|
|
161
|
+
* `tools` against `provider`.
|
|
162
|
+
*
|
|
163
|
+
* Tools without a `verb` field are skipped silently (legacy / non-SVO
|
|
164
|
+
* tools coexist with SVO-aware tools in the same registry).
|
|
165
|
+
*
|
|
166
|
+
* Conflicts (same verb name, different frame) throw on the first
|
|
167
|
+
* conflicting tool — the caller can decide whether to retry without
|
|
168
|
+
* the conflicting tool or surface the error.
|
|
169
|
+
*
|
|
170
|
+
* @returns The list of Verbs that were registered (or already existed,
|
|
171
|
+
* for idempotent calls). Tools with no verb are not included.
|
|
172
|
+
*
|
|
173
|
+
* @example Startup
|
|
174
|
+
* ```ts
|
|
175
|
+
* import { MemoryProvider, registerToolVerbs, getBuiltinTools } from 'digital-tools'
|
|
176
|
+
*
|
|
177
|
+
* const provider = new MemoryProvider()
|
|
178
|
+
* await registerToolVerbs(provider, getBuiltinTools())
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
export async function registerToolVerbs(
|
|
182
|
+
provider: VerbRegistrationProvider,
|
|
183
|
+
tools: ReadonlyArray<Tool<unknown, unknown> | AnyTool>
|
|
184
|
+
): Promise<Verb[]> {
|
|
185
|
+
const registered: Verb[] = []
|
|
186
|
+
for (const tool of tools) {
|
|
187
|
+
const verb = await registerToolVerb(provider, tool)
|
|
188
|
+
if (verb) registered.push(verb)
|
|
189
|
+
}
|
|
190
|
+
return registered
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Alias for {@link registerToolVerbs}. The "bootstrap" name reads better
|
|
195
|
+
* at startup sites where a list of tools is wired against a provider once.
|
|
196
|
+
*/
|
|
197
|
+
export const bootstrapTools = registerToolVerbs
|