@setluca/mcp 0.1.1 → 0.2.1
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 +110 -0
- package/README.md +178 -80
- package/dist/annotations.js +45 -0
- package/dist/completions.js +110 -0
- package/dist/config.js +55 -29
- package/dist/errors.js +56 -15
- package/dist/generated/openapi-inputs.js +4324 -1429
- package/dist/generated/openapi-outputs.js +21992 -8516
- package/dist/generated/route-catalog.js +1480 -0
- package/dist/http.js +97 -103
- package/dist/index.js +16 -10
- package/dist/oauth-resolver.js +59 -37
- package/dist/observability.js +12 -4
- package/dist/openapi-zod.js +138 -72
- package/dist/operations/catalog.js +50 -0
- package/dist/operations/groups/analytics.js +122 -0
- package/dist/operations/groups/bookings.js +189 -0
- package/dist/operations/groups/broadcasts.js +106 -0
- package/dist/operations/groups/cadences.js +14 -0
- package/dist/operations/groups/callEvents.js +131 -0
- package/dist/operations/groups/campaigns.js +132 -0
- package/dist/operations/groups/capabilities.js +8 -0
- package/dist/operations/groups/coach.js +43 -0
- package/dist/operations/groups/conversations.js +48 -0
- package/dist/operations/groups/insights.js +33 -0
- package/dist/operations/groups/integrations.js +80 -0
- package/dist/operations/groups/knowledge.js +34 -0
- package/dist/operations/groups/leads.js +101 -0
- package/dist/operations/groups/learning.js +24 -0
- package/dist/operations/groups/memory.js +27 -0
- package/dist/operations/groups/reports.js +14 -0
- package/dist/operations/groups/reviewQueue.js +57 -0
- package/dist/operations/groups/safety.js +8 -0
- package/dist/operations/groups/usage.js +13 -0
- package/dist/operations/groups/voice.js +14 -0
- package/dist/operations/groups/webhooks.js +76 -0
- package/dist/operations/manifest.js +40 -0
- package/dist/operations/registry.js +331 -0
- package/dist/operations.js +11 -780
- package/dist/optional-field.js +12 -0
- package/dist/page-shape.js +62 -0
- package/dist/pagination.js +165 -0
- package/dist/prompts.js +89 -8
- package/dist/provenance.js +18 -2
- package/dist/remote.js +80 -49
- package/dist/resilience.js +127 -0
- package/dist/resources.js +17 -2
- package/dist/scopes.js +154 -0
- package/dist/serialization.js +36 -0
- package/dist/server.js +258 -316
- package/dist/task-tools/analytics.js +168 -0
- package/dist/task-tools/bookings.js +157 -0
- package/dist/task-tools/cadences.js +65 -0
- package/dist/task-tools/calls.js +202 -0
- package/dist/task-tools/inbox.js +120 -0
- package/dist/task-tools/leads.js +123 -0
- package/dist/task-tools/runtime.js +92 -0
- package/dist/task-tools.js +21 -0
- package/dist/tool-cache.js +39 -0
- package/dist/tool.js +228 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/version.js +14 -0
- package/package.json +38 -34
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { op } from "../registry.js";
|
|
2
|
+
export const insightsOperations = [
|
|
3
|
+
op({
|
|
4
|
+
id: "insights.unreadCount",
|
|
5
|
+
title: "Count unread insights",
|
|
6
|
+
description: "How many insights the coach has not read yet. Cheap enough to poll before pulling the whole feed.",
|
|
7
|
+
}),
|
|
8
|
+
op({
|
|
9
|
+
id: "insights.markRead",
|
|
10
|
+
title: "Mark an insight read",
|
|
11
|
+
description: "Mark one insight read, so it stops coming back at the top of the feed.",
|
|
12
|
+
}),
|
|
13
|
+
op({
|
|
14
|
+
id: "insights.dismiss",
|
|
15
|
+
title: "Dismiss an insight",
|
|
16
|
+
description: "Dismiss one insight the coach does not want to act on. Use this rather than leaving it unread forever.",
|
|
17
|
+
}),
|
|
18
|
+
op({
|
|
19
|
+
id: "insights.markActioned",
|
|
20
|
+
title: "Mark an insight actioned",
|
|
21
|
+
description: "Record that the thing an insight asked for was actually done. Use it after doing the work, not instead of it.",
|
|
22
|
+
}),
|
|
23
|
+
op({
|
|
24
|
+
id: "insights.settings.get",
|
|
25
|
+
title: "Read alert delivery settings",
|
|
26
|
+
description: "Read which insight alerts reach the coach and how. The row is created with defaults on the first read, so this never 404s. Check it before concluding a quiet feed means nothing is happening \u2014 it may just be muted.",
|
|
27
|
+
}),
|
|
28
|
+
op({
|
|
29
|
+
id: "insights.settings.update",
|
|
30
|
+
title: "Update alert delivery settings",
|
|
31
|
+
description: "Merge-patch the coach's alert-delivery settings. Send only what you are changing; anything left out keeps its current value.",
|
|
32
|
+
}),
|
|
33
|
+
];
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { op } from "../registry.js";
|
|
2
|
+
export const integrationsOperations = [
|
|
3
|
+
op({
|
|
4
|
+
id: "integrations.connectors.list",
|
|
5
|
+
title: "List available connectors",
|
|
6
|
+
description: "List Zapier, Make, and CRM provider capabilities.",
|
|
7
|
+
}),
|
|
8
|
+
op({
|
|
9
|
+
id: "integrations.crm.oauthUrl",
|
|
10
|
+
title: "Start a CRM connection",
|
|
11
|
+
description: "Create a CRM provider OAuth URL.",
|
|
12
|
+
}),
|
|
13
|
+
op({
|
|
14
|
+
id: "integrations.crm.oauthCallback",
|
|
15
|
+
title: "Finish a CRM connection",
|
|
16
|
+
description: "Exchange a CRM OAuth code and store encrypted credentials.",
|
|
17
|
+
}),
|
|
18
|
+
op({
|
|
19
|
+
id: "integrations.crm.connections.list",
|
|
20
|
+
title: "List CRM connections",
|
|
21
|
+
description: "List CRM connections.",
|
|
22
|
+
}),
|
|
23
|
+
op({
|
|
24
|
+
id: "integrations.crm.connections.create",
|
|
25
|
+
title: "Create a CRM connection",
|
|
26
|
+
description: "Create or update a workspace-scoped CRM connection.",
|
|
27
|
+
}),
|
|
28
|
+
op({
|
|
29
|
+
id: "integrations.crm.connections.schema",
|
|
30
|
+
title: "Read a CRM's field schema",
|
|
31
|
+
description: "Discover CRM provider schema.",
|
|
32
|
+
redactedFields: ["sampleRecords"],
|
|
33
|
+
}),
|
|
34
|
+
op({
|
|
35
|
+
id: "integrations.crm.connections.health",
|
|
36
|
+
title: "Check a CRM connection's health",
|
|
37
|
+
description: "Probe CRM provider health.",
|
|
38
|
+
}),
|
|
39
|
+
op({
|
|
40
|
+
id: "integrations.crm.connections.webhooksSetup",
|
|
41
|
+
title: "Set up a CRM's webhooks",
|
|
42
|
+
description: "Configure CRM provider webhook status.",
|
|
43
|
+
}),
|
|
44
|
+
op({
|
|
45
|
+
id: "integrations.crm.webhooks.record",
|
|
46
|
+
title: "Record a CRM webhook",
|
|
47
|
+
description: "Record an inbound CRM provider webhook event. Idempotent on the provider's event id, so a redelivery is recorded once.",
|
|
48
|
+
}),
|
|
49
|
+
op({
|
|
50
|
+
id: "integrations.crm.mappings.suggest",
|
|
51
|
+
title: "Suggest CRM field mappings",
|
|
52
|
+
description: "Suggest CRM field mappings.",
|
|
53
|
+
}),
|
|
54
|
+
op({
|
|
55
|
+
id: "integrations.crm.mappings.confirm",
|
|
56
|
+
title: "Confirm CRM field mappings",
|
|
57
|
+
description: "Confirm CRM field mappings.",
|
|
58
|
+
}),
|
|
59
|
+
op({
|
|
60
|
+
id: "integrations.crm.syncRuns.list",
|
|
61
|
+
title: "List CRM sync runs",
|
|
62
|
+
description: "List CRM sync history.",
|
|
63
|
+
redactedFields: [
|
|
64
|
+
"syncRuns[].appliedMutations[].fields",
|
|
65
|
+
"syncRuns[].plannedMutations[].fields",
|
|
66
|
+
],
|
|
67
|
+
}),
|
|
68
|
+
op({
|
|
69
|
+
id: "integrations.crm.syncRuns.create",
|
|
70
|
+
title: "Start a CRM sync",
|
|
71
|
+
description: "Create a CRM dry-run or live sync run.",
|
|
72
|
+
redactedFields: ["appliedMutations[].fields", "plannedMutations[].fields"],
|
|
73
|
+
}),
|
|
74
|
+
op({
|
|
75
|
+
id: "integrations.events.list",
|
|
76
|
+
title: "List integration events",
|
|
77
|
+
description: "List integration event log.",
|
|
78
|
+
redactedFields: ["events[].payload"],
|
|
79
|
+
}),
|
|
80
|
+
];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { op } from "../registry.js";
|
|
2
|
+
export const knowledgeOperations = [
|
|
3
|
+
op({
|
|
4
|
+
id: "knowledge.suggestions",
|
|
5
|
+
title: "List knowledge suggestions",
|
|
6
|
+
description: "List profile changes proposed from the coach's own knowledge sources, each with the excerpt it was drawn from and the value it would replace.",
|
|
7
|
+
}),
|
|
8
|
+
op({
|
|
9
|
+
id: "knowledge.approveSuggestion",
|
|
10
|
+
title: "Approve a knowledge suggestion",
|
|
11
|
+
confirmRequired: true,
|
|
12
|
+
description: "Approve a profile suggestion and write it into the coach's profile. Every later draft reads the result, so check the excerpt supports the change before approving.",
|
|
13
|
+
}),
|
|
14
|
+
op({
|
|
15
|
+
id: "knowledge.rejectSuggestion",
|
|
16
|
+
title: "Reject a knowledge suggestion",
|
|
17
|
+
description: "Reject a profile suggestion so it stops being offered. The profile is left as it stands.",
|
|
18
|
+
}),
|
|
19
|
+
op({
|
|
20
|
+
id: "knowledge.sources.list",
|
|
21
|
+
title: "List knowledge sources",
|
|
22
|
+
description: "List the content sources the coach has connected \u2014 Notion, Google Docs, a website, an Instagram bio \u2014 with the sync state of each one. Connecting a new source is app-only; this lists what is already there.",
|
|
23
|
+
}),
|
|
24
|
+
op({
|
|
25
|
+
id: "knowledge.sources.sync",
|
|
26
|
+
title: "Re-sync a knowledge source",
|
|
27
|
+
description: "Queue a re-read of one knowledge source now rather than waiting for its next scheduled sync. It returns as soon as the job is queued, so any new suggestions appear on a later read.",
|
|
28
|
+
}),
|
|
29
|
+
op({
|
|
30
|
+
id: "knowledge.sources.disconnect",
|
|
31
|
+
title: "Disconnect a knowledge source",
|
|
32
|
+
description: "Delete a knowledge source. Its pending suggestions go with it; values the coach already approved stay in the profile, because approving copies them there.",
|
|
33
|
+
}),
|
|
34
|
+
];
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { op } from "../registry.js";
|
|
2
|
+
export const leadsOperations = [
|
|
3
|
+
op({
|
|
4
|
+
id: "leads.list",
|
|
5
|
+
title: "List leads",
|
|
6
|
+
description: "List leads. Query supports limit, cursor, channel, and q according to Luca OpenAPI.",
|
|
7
|
+
redactedFields: ["leads[].displayName", "leads[].externalUserId"],
|
|
8
|
+
}),
|
|
9
|
+
op({
|
|
10
|
+
id: "leads.get",
|
|
11
|
+
title: "Get a lead's timeline",
|
|
12
|
+
description: "Get a lead's full timeline and notes.",
|
|
13
|
+
redactedFields: [
|
|
14
|
+
"identities[].displayName",
|
|
15
|
+
"identities[].externalUserId",
|
|
16
|
+
"lead.customFields",
|
|
17
|
+
"lead.displayName",
|
|
18
|
+
"messages[].body",
|
|
19
|
+
"messages[].media",
|
|
20
|
+
"notes[].body",
|
|
21
|
+
],
|
|
22
|
+
}),
|
|
23
|
+
op({
|
|
24
|
+
id: "leads.explain",
|
|
25
|
+
title: "Explain why a lead qualified",
|
|
26
|
+
description: "Explain why a lead qualified: the qualifier's reasoning, signals, and confidence from its most recent drafted turn.",
|
|
27
|
+
redactedFields: ["qualification"],
|
|
28
|
+
}),
|
|
29
|
+
op({
|
|
30
|
+
id: "leads.create",
|
|
31
|
+
title: "Create or upsert a lead",
|
|
32
|
+
description: "Create or upsert a lead identity. Body requires channel and externalUserId.",
|
|
33
|
+
redactedFields: [
|
|
34
|
+
"lead.customFields",
|
|
35
|
+
"lead.displayName",
|
|
36
|
+
"lead.externalUserId",
|
|
37
|
+
],
|
|
38
|
+
}),
|
|
39
|
+
op({
|
|
40
|
+
id: "leads.update",
|
|
41
|
+
title: "Update a lead",
|
|
42
|
+
description: "Update a lead's display name, stage, score, tags, or custom fields.",
|
|
43
|
+
redactedFields: [
|
|
44
|
+
"lead.customFields",
|
|
45
|
+
"lead.displayName",
|
|
46
|
+
"lead.externalUserId",
|
|
47
|
+
],
|
|
48
|
+
}),
|
|
49
|
+
op({
|
|
50
|
+
id: "leads.fieldDefinitions.list",
|
|
51
|
+
title: "List custom lead fields",
|
|
52
|
+
description: "List custom lead field definitions.",
|
|
53
|
+
}),
|
|
54
|
+
op({
|
|
55
|
+
id: "leads.fieldDefinitions.upsert",
|
|
56
|
+
title: "Define a custom lead field",
|
|
57
|
+
description: "Create or update a custom lead field definition.",
|
|
58
|
+
}),
|
|
59
|
+
op({
|
|
60
|
+
id: "leads.importPreview",
|
|
61
|
+
title: "Preview a lead import",
|
|
62
|
+
description: "Preview a mapped CSV lead import before applying it.",
|
|
63
|
+
}),
|
|
64
|
+
op({
|
|
65
|
+
id: "leads.importApply",
|
|
66
|
+
title: "Apply a lead import",
|
|
67
|
+
description: "Apply a mapped lead import with consent evidence.",
|
|
68
|
+
}),
|
|
69
|
+
op({
|
|
70
|
+
id: "leads.identities.attach",
|
|
71
|
+
title: "Attach a channel identity to a lead",
|
|
72
|
+
description: "Attach a verified or claimed identity to a lead.",
|
|
73
|
+
}),
|
|
74
|
+
op({
|
|
75
|
+
id: "leads.consents.grant",
|
|
76
|
+
title: "Record a lead's consent",
|
|
77
|
+
description: "Grant explicit lead consent for a channel purpose.",
|
|
78
|
+
}),
|
|
79
|
+
op({
|
|
80
|
+
id: "leads.consents.revoke",
|
|
81
|
+
title: "Revoke a lead's consent",
|
|
82
|
+
description: "Revoke explicit lead consent for a channel purpose.",
|
|
83
|
+
}),
|
|
84
|
+
op({
|
|
85
|
+
id: "leads.notes.add",
|
|
86
|
+
title: "Add a note to a lead",
|
|
87
|
+
description: "Append a coach note to a lead. Body requires body.",
|
|
88
|
+
redactedFields: ["note.body"],
|
|
89
|
+
}),
|
|
90
|
+
op({
|
|
91
|
+
id: "leads.draft",
|
|
92
|
+
title: "Draft a reply to a lead",
|
|
93
|
+
description: "Draft a voice-matched reply for a lead on demand and queue it for review. Optional guidance steers tone or content.",
|
|
94
|
+
redactedFields: ["draft.body"],
|
|
95
|
+
}),
|
|
96
|
+
op({
|
|
97
|
+
id: "leads.notes.delete",
|
|
98
|
+
title: "Delete a lead note",
|
|
99
|
+
description: "Remove one coach note from a lead and leave the rest of the timeline alone. This is how a note filed by mistake gets taken back.",
|
|
100
|
+
}),
|
|
101
|
+
];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { op } from "../registry.js";
|
|
2
|
+
export const learningOperations = [
|
|
3
|
+
op({
|
|
4
|
+
id: "learning.panel",
|
|
5
|
+
title: "Read what Luca wants to learn",
|
|
6
|
+
description: "Read which of the three profile questions Luca still owes an answer to, with suggested answers inferred from the coach's own words. The worked example pair is blanked for a redacted-tier key.",
|
|
7
|
+
redactedFields: ["exampleDm", "exampleDraft"],
|
|
8
|
+
}),
|
|
9
|
+
op({
|
|
10
|
+
id: "learning.frameworkPerformance",
|
|
11
|
+
title: "Read framework objection performance",
|
|
12
|
+
description: "Win rate by objection bucket for the sales framework the coach has configured right now, over a rolling window. These numbers are correlational \u2014 read them as a signal, not as proof a framework caused the result.",
|
|
13
|
+
}),
|
|
14
|
+
op({
|
|
15
|
+
id: "learning.proposals.list",
|
|
16
|
+
title: "List learning proposals",
|
|
17
|
+
description: "List what the coach's tests have learned and are proposing they act on, newest first. Dismissed proposals are left out unless you ask for them.",
|
|
18
|
+
}),
|
|
19
|
+
op({
|
|
20
|
+
id: "learning.proposals.dismiss",
|
|
21
|
+
title: "Dismiss a learning proposal",
|
|
22
|
+
description: "Dismiss one learning proposal so it drops out of the default list. Use it when the coach has decided against the change, not when you have simply read it.",
|
|
23
|
+
}),
|
|
24
|
+
];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { op } from "../registry.js";
|
|
2
|
+
export const memoryOperations = [
|
|
3
|
+
op({
|
|
4
|
+
id: "memory.list",
|
|
5
|
+
title: "List what Luca remembers",
|
|
6
|
+
description: "List what Luca remembers, filtered by lead, scope, source, trust level, or free text. The fact, the reasoning, and the quoted evidence are blanked for a redacted-tier key; trust level, lead, and dates always come back.",
|
|
7
|
+
redactedFields: [
|
|
8
|
+
"memories[].content",
|
|
9
|
+
"memories[].evidence",
|
|
10
|
+
"memories[].reason",
|
|
11
|
+
],
|
|
12
|
+
}),
|
|
13
|
+
op({
|
|
14
|
+
id: "memory.update",
|
|
15
|
+
title: "Edit a memory",
|
|
16
|
+
// The corrected memory comes back in full, and a memory is a fact Luca
|
|
17
|
+
// extracted from what a lead wrote.
|
|
18
|
+
untrustedContent: true,
|
|
19
|
+
description: "Correct one memory: edit the fact, flag it disputed when it turns out to be wrong, restore an archived one, or make it win over a conflicting memory.",
|
|
20
|
+
redactedFields: ["content", "evidence", "reason"],
|
|
21
|
+
}),
|
|
22
|
+
op({
|
|
23
|
+
id: "memory.setArchived",
|
|
24
|
+
title: "Archive or restore memories",
|
|
25
|
+
description: "Archive or restore several memories at once. Archiving stops Luca using a fact while the record and its provenance survive, so it is always reversible.",
|
|
26
|
+
}),
|
|
27
|
+
];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { op } from "../registry.js";
|
|
2
|
+
export const reportsOperations = [
|
|
3
|
+
op({
|
|
4
|
+
id: "reports.scorecard",
|
|
5
|
+
title: "Read the scorecard",
|
|
6
|
+
description: "Read the sales scorecard for a window: calls booked and completed, show rate, won and lost, draft approvals, and hours saved.",
|
|
7
|
+
}),
|
|
8
|
+
op({
|
|
9
|
+
id: "reports.morning",
|
|
10
|
+
title: "Read the morning report",
|
|
11
|
+
description: "Structured 24h review-queue digest (items + counts) for the coach. No model call; summarize client-side.",
|
|
12
|
+
redactedFields: ["items[].draftBody"],
|
|
13
|
+
}),
|
|
14
|
+
];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { op } from "../registry.js";
|
|
2
|
+
export const reviewQueueOperations = [
|
|
3
|
+
op({
|
|
4
|
+
id: "reviewQueue.list",
|
|
5
|
+
title: "List the review queue",
|
|
6
|
+
description: "List review-queue items when the API key has review queue access.",
|
|
7
|
+
redactedFields: [
|
|
8
|
+
"items[].criticIssues",
|
|
9
|
+
"items[].draftBody",
|
|
10
|
+
"items[].draftParts",
|
|
11
|
+
"items[].leadDisplayName",
|
|
12
|
+
"items[].media",
|
|
13
|
+
"items[].mediaContext",
|
|
14
|
+
"items[].objectionVariants",
|
|
15
|
+
"items[].qualification",
|
|
16
|
+
"items[].safetyFindings",
|
|
17
|
+
"items[].signals",
|
|
18
|
+
],
|
|
19
|
+
}),
|
|
20
|
+
op({
|
|
21
|
+
id: "reviewQueue.explain",
|
|
22
|
+
title: "Explain a queued draft",
|
|
23
|
+
description: "Explain the signals behind a drafted reply: its voice-match score, any critic findings, and the qualification that motivated it.",
|
|
24
|
+
redactedFields: ["criticIssues", "draftBody", "qualification"],
|
|
25
|
+
}),
|
|
26
|
+
op({
|
|
27
|
+
id: "reviewQueue.approve",
|
|
28
|
+
title: "Approve and send a queued draft",
|
|
29
|
+
confirmRequired: true,
|
|
30
|
+
description: "Approve a pending draft for sending; dispatches through the lead's channel. Sends a real message.",
|
|
31
|
+
}),
|
|
32
|
+
op({
|
|
33
|
+
id: "reviewQueue.reject",
|
|
34
|
+
title: "Reject a queued draft",
|
|
35
|
+
description: "Reject a pending review-queue draft so it never sends. Stops any related booking-recovery cadence and emits review.rejected.",
|
|
36
|
+
}),
|
|
37
|
+
op({
|
|
38
|
+
id: "reviewQueue.restore",
|
|
39
|
+
title: "Restore a rejected draft",
|
|
40
|
+
description: "Restore a rejected review-queue item back to pending. Fails if it's a booking message whose recovery window already passed.",
|
|
41
|
+
}),
|
|
42
|
+
op({
|
|
43
|
+
id: "reviewQueue.media.retry",
|
|
44
|
+
title: "Retry a draft's failed media",
|
|
45
|
+
description: "Retry a failed voice-note transcription so the draft can be reviewed. Only re-transcribes; never redrafts or sends.",
|
|
46
|
+
}),
|
|
47
|
+
op({
|
|
48
|
+
id: "reviewQueue.sla",
|
|
49
|
+
title: "Check the review queue's response times",
|
|
50
|
+
description: "Read median first-response time and its daily trend. Tells you whether the queue is being cleared fast enough.",
|
|
51
|
+
}),
|
|
52
|
+
op({
|
|
53
|
+
id: "reviewQueue.objectionVariant",
|
|
54
|
+
title: "Record an objection variant choice",
|
|
55
|
+
description: "Record which objection-specialist variant was picked for one queued draft. It is how the objection tests learn which handling works, so record the variant the coach actually chose.",
|
|
56
|
+
}),
|
|
57
|
+
];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { op } from "../registry.js";
|
|
2
|
+
export const safetyOperations = [
|
|
3
|
+
op({
|
|
4
|
+
id: "safety.accounts",
|
|
5
|
+
title: "Check connected account safety",
|
|
6
|
+
description: "Read each connected channel account's safety status, 24-hour send count, and human-approval window. An account inside its approval window must not be automated against.",
|
|
7
|
+
}),
|
|
8
|
+
];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { op } from "../registry.js";
|
|
2
|
+
export const usageOperations = [
|
|
3
|
+
op({
|
|
4
|
+
id: "usage.spend",
|
|
5
|
+
title: "Read this period's spend",
|
|
6
|
+
description: "Read model spend for the period, rolled up by agent and by day, against the plan's cap.",
|
|
7
|
+
}),
|
|
8
|
+
op({
|
|
9
|
+
id: "usage.messages",
|
|
10
|
+
title: "Read this period's message count",
|
|
11
|
+
description: "Read the message pool: used, allowed, and remaining for the period. Check this before starting work that drafts in bulk.",
|
|
12
|
+
}),
|
|
13
|
+
];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { op } from "../registry.js";
|
|
2
|
+
export const voiceOperations = [
|
|
3
|
+
op({
|
|
4
|
+
id: "voice.profile",
|
|
5
|
+
title: "Read the coach's voice fingerprint",
|
|
6
|
+
description: "Read a non-verbatim voice-fingerprint summary: version, example count, and last refresh. No corpus text.",
|
|
7
|
+
}),
|
|
8
|
+
op({
|
|
9
|
+
id: "voice.corpus",
|
|
10
|
+
title: "Read the coach's voice corpus",
|
|
11
|
+
description: "List the coach's voice corpus: the writing every draft is matched against. Entry id, source, channel, and age always come back; the writing itself is blanked for a redacted-tier key.",
|
|
12
|
+
redactedFields: ["entries[].content"],
|
|
13
|
+
}),
|
|
14
|
+
];
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { op } from "../registry.js";
|
|
2
|
+
export const webhooksOperations = [
|
|
3
|
+
op({
|
|
4
|
+
id: "webhooks.subscriptions.list",
|
|
5
|
+
title: "List webhook subscriptions",
|
|
6
|
+
description: "List outbound webhook subscriptions.",
|
|
7
|
+
}),
|
|
8
|
+
op({
|
|
9
|
+
id: "webhooks.subscriptions.create",
|
|
10
|
+
title: "Create a webhook subscription",
|
|
11
|
+
// Naturally idempotent: an active subscription with the same target URL
|
|
12
|
+
// is updated in place (target URLs are unique per Zap/scenario webhook),
|
|
13
|
+
// so this route requires no Idempotency-Key (see the matching comment on
|
|
14
|
+
// its policy entry in apps/api/src/lib/public-route-policy.ts).
|
|
15
|
+
description: "Create an outbound webhook subscription.",
|
|
16
|
+
}),
|
|
17
|
+
op({
|
|
18
|
+
id: "webhooks.subscriptions.update",
|
|
19
|
+
title: "Update a webhook subscription",
|
|
20
|
+
description: "Update an outbound webhook subscription.",
|
|
21
|
+
}),
|
|
22
|
+
op({
|
|
23
|
+
id: "webhooks.subscriptions.delete",
|
|
24
|
+
title: "Delete a webhook subscription",
|
|
25
|
+
// Deleting by id is naturally idempotent (the route matches its policy
|
|
26
|
+
// entry, which carries no requireIdempotency), so no Idempotency-Key is
|
|
27
|
+
// sent and it stays out of the confirm-gated tier that re-triggers live
|
|
28
|
+
// downstream automation; re-running the delete is a safe no-op.
|
|
29
|
+
description: "Delete an outbound webhook subscription. Terminal, idempotent soft delete: delivery history stays queryable and the subscription can never be resurrected.",
|
|
30
|
+
}),
|
|
31
|
+
op({
|
|
32
|
+
id: "webhooks.testDelivery",
|
|
33
|
+
title: "Send a test webhook",
|
|
34
|
+
description: "Queue a canonical test webhook event.",
|
|
35
|
+
}),
|
|
36
|
+
op({
|
|
37
|
+
id: "webhooks.deliveries.list",
|
|
38
|
+
title: "List webhook deliveries",
|
|
39
|
+
description: "List webhook deliveries.",
|
|
40
|
+
}),
|
|
41
|
+
op({
|
|
42
|
+
id: "webhooks.signatureGuide",
|
|
43
|
+
title: "Read the webhook signature guide",
|
|
44
|
+
description: "Describe Luca webhook HMAC verification.",
|
|
45
|
+
}),
|
|
46
|
+
op({
|
|
47
|
+
id: "webhooks.deliveries.replay",
|
|
48
|
+
title: "Replay a webhook delivery",
|
|
49
|
+
confirmRequired: true,
|
|
50
|
+
description: "Replay one webhook delivery without changing the event id. Re-triggers the subscriber's downstream automation.",
|
|
51
|
+
}),
|
|
52
|
+
op({
|
|
53
|
+
id: "webhooks.events.list",
|
|
54
|
+
title: "List webhook events",
|
|
55
|
+
description: "List canonical webhook events of one type, newest first, in the delivered payload fields. Falls back to a documented sample payload when the workspace has none yet.",
|
|
56
|
+
// Not framed untrusted: every event's delivered payload is structured ids,
|
|
57
|
+
// enums, and counts (e.g. `message.received` is
|
|
58
|
+
// `{ leadId, conversationId, channel, providerMessageId }`). No verbatim
|
|
59
|
+
// lead text, draft body, or PII. The one content-shaped field in the whole
|
|
60
|
+
// catalog is a fictional `text` in the "no events yet" sample fallback,
|
|
61
|
+
// which the live pipeline never emits. Contrast `campaigns.commentEvents.list`,
|
|
62
|
+
// which reads real verbatim commenter text and stays untrusted.
|
|
63
|
+
}),
|
|
64
|
+
op({
|
|
65
|
+
id: "webhooks.events.replay",
|
|
66
|
+
title: "Replay a webhook event",
|
|
67
|
+
confirmRequired: true,
|
|
68
|
+
description: "Replay one canonical webhook event. Re-triggers subscribers' downstream automations.",
|
|
69
|
+
}),
|
|
70
|
+
op({
|
|
71
|
+
id: "webhooks.events.bulkReplay",
|
|
72
|
+
title: "Replay a batch of webhook events",
|
|
73
|
+
confirmRequired: true,
|
|
74
|
+
description: "Bulk replay webhook events by type and time range. Re-triggers downstream automations at scale.",
|
|
75
|
+
}),
|
|
76
|
+
];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Views derived from the operation table. Nothing here holds state of its own;
|
|
2
|
+
// each function reads `LUCA_OPERATIONS` and reshapes it for one caller: the
|
|
3
|
+
// `luca://operations` resource, the docs generator, or the registry check.
|
|
4
|
+
import { operationAnnotations } from "../annotations.js";
|
|
5
|
+
import { LUCA_OPERATIONS } from "./catalog.js";
|
|
6
|
+
import { LUCA_OPERATION_GROUPS } from "./registry.js";
|
|
7
|
+
export function operationManifest() {
|
|
8
|
+
return LUCA_OPERATIONS.map((operation) => ({
|
|
9
|
+
id: operation.id,
|
|
10
|
+
group: operation.group,
|
|
11
|
+
toolName: operation.toolName,
|
|
12
|
+
method: operation.method,
|
|
13
|
+
path: operation.path,
|
|
14
|
+
scopes: operation.scopes,
|
|
15
|
+
idempotencyRequired: operation.idempotencyRequired,
|
|
16
|
+
confirmRequired: operation.confirmRequired,
|
|
17
|
+
paginates: operation.pageShape !== undefined,
|
|
18
|
+
untrustedContent: operation.untrustedContent,
|
|
19
|
+
requiredScope: operation.requiredScope,
|
|
20
|
+
annotations: operationAnnotations(operation),
|
|
21
|
+
description: operation.description,
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
export function operationGroups() {
|
|
25
|
+
return Object.entries(LUCA_OPERATION_GROUPS).map(([id, metadata]) => {
|
|
26
|
+
const operations = LUCA_OPERATIONS.filter((operation) => operation.group === id);
|
|
27
|
+
return {
|
|
28
|
+
// SAFETY: Object.entries widens the key to `string`; the cast recovers
|
|
29
|
+
// what TypeScript already knows about LUCA_OPERATION_GROUPS's own keys.
|
|
30
|
+
id: id,
|
|
31
|
+
title: metadata.title,
|
|
32
|
+
description: metadata.description,
|
|
33
|
+
toolCount: operations.length,
|
|
34
|
+
scopes: [
|
|
35
|
+
...new Set(operations.flatMap((operation) => operation.scopes)),
|
|
36
|
+
].sort(),
|
|
37
|
+
tools: operations.map((operation) => operation.toolName),
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
}
|