@sellable/mcp 0.1.549 → 0.1.550
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/dist/api.js +6 -3
- package/dist/auth.d.ts +6 -0
- package/dist/auth.js +44 -2
- package/dist/index-dev.js +0 -0
- package/dist/index.js +0 -0
- package/dist/refill-date-window.d.ts +34 -0
- package/dist/refill-date-window.js +210 -0
- package/dist/server.js +1 -1
- package/dist/tools/auth.d.ts +5 -0
- package/dist/tools/auth.js +49 -12
- package/dist/tools/campaigns.d.ts +8 -57
- package/dist/tools/campaigns.js +5 -30
- package/dist/tools/csv-dnc.js +2 -2
- package/dist/tools/leads.d.ts +0 -22
- package/dist/tools/leads.js +1 -6
- package/dist/tools/refill-sends-evergreen.d.ts +28 -0
- package/dist/tools/refill-sends-evergreen.js +47 -0
- package/dist/tools/registry.d.ts +9 -88
- package/dist/tools/sequencer.d.ts +3 -16
- package/dist/tools/sequencer.js +36 -72
- package/dist/tools/setup-evergreen-campaigns.d.ts +0 -21
- package/dist/tools/setup-evergreen-campaigns.js +3 -22
- package/dist/tools/workspace-context.d.ts +1 -1
- package/dist/tools/workspace-context.js +8 -3
- package/dist/tools/workspace-export.js +2 -2
- package/dist/tools/workspaces.d.ts +48 -2
- package/dist/tools/workspaces.js +48 -5
- package/package.json +1 -1
- package/skills/create-campaign-v2/references/tier-routing-matrix.md +0 -5
- package/skills/create-evergreen-campaigns/SKILL.md +8 -57
- package/skills/refill-sends/SKILL.md +0 -5
- package/skills/refill-sends-v2/SKILL.md +0 -5
- package/skills/refill-sends-v2-workflow/SKILL.md +0 -7
- package/skills/refill-sends-v2-workflow/core/flow.v1.json +0 -1
|
@@ -19,15 +19,12 @@ export type CreateWorkflowTableResponse = {
|
|
|
19
19
|
};
|
|
20
20
|
export type AttachSequenceInput = {
|
|
21
21
|
tableId: string;
|
|
22
|
-
template
|
|
23
|
-
templateRef?: "connection_only";
|
|
22
|
+
template: Record<string, unknown>;
|
|
24
23
|
confirmed?: boolean;
|
|
25
|
-
currentStep?: string;
|
|
26
24
|
};
|
|
27
25
|
export type AttachSequenceResponse = {
|
|
28
26
|
success: boolean;
|
|
29
27
|
version: number;
|
|
30
|
-
currentStep?: string;
|
|
31
28
|
_perf?: Record<string, number>;
|
|
32
29
|
};
|
|
33
30
|
export type AttachRecommendedSequenceInput = {
|
|
@@ -77,10 +74,9 @@ export declare const sequencerToolDefinitions: ({
|
|
|
77
74
|
};
|
|
78
75
|
tableId?: undefined;
|
|
79
76
|
template?: undefined;
|
|
80
|
-
templateRef?: undefined;
|
|
81
77
|
confirmed?: undefined;
|
|
82
|
-
currentStep?: undefined;
|
|
83
78
|
campaignId?: undefined;
|
|
79
|
+
currentStep?: undefined;
|
|
84
80
|
};
|
|
85
81
|
required: string[];
|
|
86
82
|
};
|
|
@@ -98,25 +94,17 @@ export declare const sequencerToolDefinitions: ({
|
|
|
98
94
|
type: string;
|
|
99
95
|
description: string;
|
|
100
96
|
};
|
|
101
|
-
templateRef: {
|
|
102
|
-
type: string;
|
|
103
|
-
enum: string[];
|
|
104
|
-
description: string;
|
|
105
|
-
};
|
|
106
97
|
confirmed: {
|
|
107
98
|
type: string;
|
|
108
99
|
description: string;
|
|
109
100
|
};
|
|
110
|
-
currentStep: {
|
|
111
|
-
type: string;
|
|
112
|
-
description: string;
|
|
113
|
-
};
|
|
114
101
|
name?: undefined;
|
|
115
102
|
senderId?: undefined;
|
|
116
103
|
clientProspectId?: undefined;
|
|
117
104
|
senderLinkedinUrl?: undefined;
|
|
118
105
|
sequenceActions?: undefined;
|
|
119
106
|
campaignId?: undefined;
|
|
107
|
+
currentStep?: undefined;
|
|
120
108
|
};
|
|
121
109
|
required: string[];
|
|
122
110
|
};
|
|
@@ -145,7 +133,6 @@ export declare const sequencerToolDefinitions: ({
|
|
|
145
133
|
sequenceActions?: undefined;
|
|
146
134
|
tableId?: undefined;
|
|
147
135
|
template?: undefined;
|
|
148
|
-
templateRef?: undefined;
|
|
149
136
|
};
|
|
150
137
|
required: string[];
|
|
151
138
|
};
|
package/dist/tools/sequencer.js
CHANGED
|
@@ -53,21 +53,12 @@ export const sequencerToolDefinitions = [
|
|
|
53
53
|
type: "object",
|
|
54
54
|
description: "Sequence template object with version, entryNodeId, nodes, and branches",
|
|
55
55
|
},
|
|
56
|
-
templateRef: {
|
|
57
|
-
type: "string",
|
|
58
|
-
enum: ["connection_only"],
|
|
59
|
-
description: "Optional backend-owned known template reference. Use connection_only for invite-only evergreen setup without duplicating app template JSON in MCP.",
|
|
60
|
-
},
|
|
61
56
|
confirmed: {
|
|
62
57
|
type: "boolean",
|
|
63
58
|
description: "Set true to overwrite existing sequence columns if the table already has them",
|
|
64
59
|
},
|
|
65
|
-
currentStep: {
|
|
66
|
-
type: "string",
|
|
67
|
-
description: "Optional CampaignOffer currentStep to persist atomically with the sequence attach, usually 'send' for connection-only evergreen setup after the invite-only sequence is bound.",
|
|
68
|
-
},
|
|
69
60
|
},
|
|
70
|
-
required: ["tableId"],
|
|
61
|
+
required: ["tableId", "template"],
|
|
71
62
|
},
|
|
72
63
|
},
|
|
73
64
|
{
|
|
@@ -116,57 +107,56 @@ export async function attachSequence(input) {
|
|
|
116
107
|
const api = getApi();
|
|
117
108
|
const template = input.template;
|
|
118
109
|
const preValidationErrors = [];
|
|
119
|
-
if (
|
|
120
|
-
|
|
110
|
+
if (!Array.isArray(template.nodes) || template.nodes.length === 0) {
|
|
111
|
+
preValidationErrors.push("Template must have a non-empty 'nodes' array.");
|
|
121
112
|
}
|
|
122
|
-
if (
|
|
123
|
-
|
|
113
|
+
if (typeof template.entryNodeId !== "string" ||
|
|
114
|
+
template.entryNodeId.trim().length === 0) {
|
|
115
|
+
preValidationErrors.push("Template must have an 'entryNodeId' string.");
|
|
124
116
|
}
|
|
125
|
-
if (
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
typeof template.entryNodeId === "string") {
|
|
138
|
-
const nodeIds = new Set(template.nodes
|
|
139
|
-
.map((node) => node && typeof node === "object"
|
|
140
|
-
? node.id
|
|
141
|
-
: null)
|
|
142
|
-
.filter((nodeId) => typeof nodeId === "string"));
|
|
143
|
-
if (template.entryNodeId && !nodeIds.has(template.entryNodeId)) {
|
|
144
|
-
preValidationErrors.push(`entryNodeId "${template.entryNodeId}" does not match any node id. Available node ids: ${Array.from(nodeIds).join(", ")}`);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
if (preValidationErrors.length > 0) {
|
|
148
|
-
throw new Error("INVALID_TEMPLATE: Template has structural issues:\n" +
|
|
149
|
-
preValidationErrors.map((issue) => ` - ${issue}`).join("\n") +
|
|
150
|
-
"\n\nFix these issues and retry.");
|
|
117
|
+
if (template.version !== 2) {
|
|
118
|
+
preValidationErrors.push("Template version must be 2.");
|
|
119
|
+
}
|
|
120
|
+
if (Array.isArray(template.nodes) &&
|
|
121
|
+
typeof template.entryNodeId === "string") {
|
|
122
|
+
const nodeIds = new Set(template.nodes
|
|
123
|
+
.map((node) => node && typeof node === "object"
|
|
124
|
+
? node.id
|
|
125
|
+
: null)
|
|
126
|
+
.filter((nodeId) => typeof nodeId === "string"));
|
|
127
|
+
if (template.entryNodeId && !nodeIds.has(template.entryNodeId)) {
|
|
128
|
+
preValidationErrors.push(`entryNodeId "${template.entryNodeId}" does not match any node id. Available node ids: ${Array.from(nodeIds).join(", ")}`);
|
|
151
129
|
}
|
|
152
130
|
}
|
|
131
|
+
if (preValidationErrors.length > 0) {
|
|
132
|
+
throw new Error("INVALID_TEMPLATE: Template has structural issues:\n" +
|
|
133
|
+
preValidationErrors.map((issue) => ` - ${issue}`).join("\n") +
|
|
134
|
+
"\n\nFix these issues and retry.");
|
|
135
|
+
}
|
|
153
136
|
// Campaign-backed tables route to the campaigns endpoint so the MCP
|
|
154
137
|
// tool persists the sequence to the same place the UI does
|
|
155
138
|
// (`CampaignOffer.sequenceTemplate`). The workflow-tables endpoint
|
|
156
139
|
// writes to `WorkflowTable.config.sequenceTemplate`, which the UI
|
|
157
140
|
// doesn't read — hitting it on a campaign table is silently wrong.
|
|
158
|
-
|
|
141
|
+
let campaignOfferId = null;
|
|
142
|
+
try {
|
|
143
|
+
const tableMeta = await api.get(`/api/v3/workflow-tables/${input.tableId}?mode=meta`);
|
|
144
|
+
const rawCampaignId = tableMeta?.table?.config?.campaignOfferId;
|
|
145
|
+
if (typeof rawCampaignId === "string" && rawCampaignId.length > 0) {
|
|
146
|
+
campaignOfferId = rawCampaignId;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// Fall back to workflow-tables path on meta-fetch failure —
|
|
151
|
+
// non-campaign tables won't have the field set anyway.
|
|
152
|
+
}
|
|
159
153
|
const endpoint = campaignOfferId
|
|
160
154
|
? `/api/v3/campaigns/${campaignOfferId}/sequence`
|
|
161
155
|
: `/api/v3/workflow-tables/${input.tableId}/sequence`;
|
|
162
156
|
try {
|
|
163
157
|
return await api.put(endpoint, {
|
|
164
|
-
|
|
165
|
-
...(input.templateRef ? { templateRef: input.templateRef } : {}),
|
|
158
|
+
template: input.template,
|
|
166
159
|
confirmed: input.confirmed,
|
|
167
|
-
...(input.currentStep !== undefined
|
|
168
|
-
? { currentStep: input.currentStep }
|
|
169
|
-
: {}),
|
|
170
160
|
});
|
|
171
161
|
}
|
|
172
162
|
catch (error) {
|
|
@@ -187,32 +177,6 @@ export async function attachSequence(input) {
|
|
|
187
177
|
throw error;
|
|
188
178
|
}
|
|
189
179
|
}
|
|
190
|
-
async function resolveCampaignOfferIdForTable(api, tableId) {
|
|
191
|
-
try {
|
|
192
|
-
const tableMeta = await api.get(`/api/v3/workflow-tables/${tableId}?mode=meta`);
|
|
193
|
-
const rawCampaignId = tableMeta?.table?.config?.campaignOfferId;
|
|
194
|
-
if (typeof rawCampaignId === "string" && rawCampaignId.length > 0) {
|
|
195
|
-
return rawCampaignId;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
catch {
|
|
199
|
-
// Continue to the inventory fallback below.
|
|
200
|
-
}
|
|
201
|
-
try {
|
|
202
|
-
const tableInventory = await api.get("/api/v3/mcp/tables?limit=500");
|
|
203
|
-
const matchingTable = tableInventory?.tables?.find((table) => {
|
|
204
|
-
return table?.id === tableId;
|
|
205
|
-
});
|
|
206
|
-
const rawCampaignId = matchingTable?.campaignOfferId;
|
|
207
|
-
if (typeof rawCampaignId === "string" && rawCampaignId.length > 0) {
|
|
208
|
-
return rawCampaignId;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
catch {
|
|
212
|
-
// Fall back to workflow-tables path on inventory failure.
|
|
213
|
-
}
|
|
214
|
-
return null;
|
|
215
|
-
}
|
|
216
180
|
export async function attachRecommendedSequence(input) {
|
|
217
181
|
const api = getApi();
|
|
218
182
|
try {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
type SetupEvergreenCampaignsInput = {
|
|
2
|
-
workspaceId?: string;
|
|
3
2
|
mode?: "plan" | "verify";
|
|
4
3
|
depth?: "structure_only" | "customer_visible";
|
|
5
4
|
handoffMode?: "create_campaign_goals";
|
|
@@ -16,9 +15,6 @@ type SetupEvergreenCampaignsInput = {
|
|
|
16
15
|
planRevision?: string;
|
|
17
16
|
selectedActionIds?: string[];
|
|
18
17
|
receipts?: Array<Record<string, unknown>>;
|
|
19
|
-
campaignSequenceOptions?: {
|
|
20
|
-
mode: "connection_only";
|
|
21
|
-
};
|
|
22
18
|
};
|
|
23
19
|
export declare const setupEvergreenCampaignsToolDefinitions: {
|
|
24
20
|
name: string;
|
|
@@ -31,23 +27,6 @@ export declare const setupEvergreenCampaignsToolDefinitions: {
|
|
|
31
27
|
enum: string[];
|
|
32
28
|
description: string;
|
|
33
29
|
};
|
|
34
|
-
workspaceId: {
|
|
35
|
-
type: string;
|
|
36
|
-
description: string;
|
|
37
|
-
};
|
|
38
|
-
campaignSequenceOptions: {
|
|
39
|
-
type: string;
|
|
40
|
-
properties: {
|
|
41
|
-
mode: {
|
|
42
|
-
type: string;
|
|
43
|
-
enum: string[];
|
|
44
|
-
description: string;
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
required: string[];
|
|
48
|
-
additionalProperties: boolean;
|
|
49
|
-
description: string;
|
|
50
|
-
};
|
|
51
30
|
depth: {
|
|
52
31
|
type: string;
|
|
53
32
|
enum: string[];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getApi } from "../api.js";
|
|
2
|
-
async function postSetupEvergreenCampaigns(body
|
|
2
|
+
async function postSetupEvergreenCampaigns(body) {
|
|
3
3
|
const api = getApi();
|
|
4
|
-
return api.post("/api/v3/mcp/setup-evergreen-campaigns", body
|
|
4
|
+
return api.post("/api/v3/mcp/setup-evergreen-campaigns", body);
|
|
5
5
|
}
|
|
6
6
|
export const setupEvergreenCampaignsToolDefinitions = [
|
|
7
7
|
{
|
|
@@ -15,23 +15,6 @@ export const setupEvergreenCampaignsToolDefinitions = [
|
|
|
15
15
|
enum: ["plan", "verify"],
|
|
16
16
|
description: 'Defaults to "plan". Verify only validates worker receipts.',
|
|
17
17
|
},
|
|
18
|
-
workspaceId: {
|
|
19
|
-
type: "string",
|
|
20
|
-
description: "Optional explicit workspace id. Required for connection-only evergreen setup; it is also sent as the request workspace so concurrent threads cannot drift to another active workspace.",
|
|
21
|
-
},
|
|
22
|
-
campaignSequenceOptions: {
|
|
23
|
-
type: "object",
|
|
24
|
-
properties: {
|
|
25
|
-
mode: {
|
|
26
|
-
type: "string",
|
|
27
|
-
enum: ["connection_only"],
|
|
28
|
-
description: "Optional non-default sequence policy. connection_only means attach the canonical invite-only template and verify exactly send_invite, with no DM, InMail, View Profile, follow-up, launch, or send side effects.",
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
required: ["mode"],
|
|
32
|
-
additionalProperties: false,
|
|
33
|
-
description: "Optional non-default evergreen sequence policy. Omit to keep the standard tier-recommended Premium/Sales Nav behavior.",
|
|
34
|
-
},
|
|
35
18
|
depth: {
|
|
36
19
|
type: "string",
|
|
37
20
|
enum: ["structure_only", "customer_visible"],
|
|
@@ -107,12 +90,10 @@ export const setupEvergreenCampaignsToolDefinitions = [
|
|
|
107
90
|
];
|
|
108
91
|
export function setupEvergreenCampaigns(input) {
|
|
109
92
|
return postSetupEvergreenCampaigns({
|
|
110
|
-
workspaceId: input.workspaceId,
|
|
111
93
|
mode: input.mode,
|
|
112
94
|
yolo: input.mode === "verify" ? undefined : input.yolo,
|
|
113
95
|
depth: input.depth,
|
|
114
96
|
handoffMode: input.mode === "verify" ? undefined : input.handoffMode,
|
|
115
|
-
campaignSequenceOptions: input.campaignSequenceOptions,
|
|
116
97
|
allConnectedSenders: input.allConnectedSenders,
|
|
117
98
|
selectedSenderIds: input.selectedSenderIds,
|
|
118
99
|
postEngagerSenderIds: input.postEngagerSenderIds,
|
|
@@ -121,5 +102,5 @@ export function setupEvergreenCampaigns(input) {
|
|
|
121
102
|
planRevision: input.planRevision,
|
|
122
103
|
selectedActionIds: input.selectedActionIds,
|
|
123
104
|
receipts: input.receipts,
|
|
124
|
-
}
|
|
105
|
+
});
|
|
125
106
|
}
|
|
@@ -12,7 +12,7 @@ export interface WorkspaceContext {
|
|
|
12
12
|
executionMode: WorkspaceExecutionMode;
|
|
13
13
|
toolName?: string;
|
|
14
14
|
runId: string;
|
|
15
|
-
workspaceResolution: "explicit";
|
|
15
|
+
workspaceResolution: "explicit" | "locked_profile";
|
|
16
16
|
}
|
|
17
17
|
export interface WorkspaceRequiredResult {
|
|
18
18
|
ok: false;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { getLockedWorkspaceId, resolveWorkspaceIdForRequest, } from "../auth.js";
|
|
2
3
|
export function workspaceRequired(params) {
|
|
3
4
|
const toolLabel = params.toolName ? ` for ${params.toolName}` : "";
|
|
4
5
|
return {
|
|
@@ -15,11 +16,13 @@ export function normalizeExplicitWorkspaceId(value) {
|
|
|
15
16
|
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
16
17
|
}
|
|
17
18
|
export function workspaceRequestOptions(workspaceId) {
|
|
18
|
-
const normalized = normalizeExplicitWorkspaceId(workspaceId);
|
|
19
|
+
const normalized = resolveWorkspaceIdForRequest(normalizeExplicitWorkspaceId(workspaceId), "workspace request options");
|
|
19
20
|
return normalized ? Object.freeze({ workspaceId: normalized }) : undefined;
|
|
20
21
|
}
|
|
21
22
|
export function createWorkspaceContext(input) {
|
|
22
|
-
const
|
|
23
|
+
const explicitWorkspaceId = normalizeExplicitWorkspaceId(input.workspaceId);
|
|
24
|
+
const lockedWorkspaceId = getLockedWorkspaceId();
|
|
25
|
+
const workspaceId = resolveWorkspaceIdForRequest(explicitWorkspaceId, input.toolName || "workspace context");
|
|
23
26
|
if (!workspaceId) {
|
|
24
27
|
return workspaceRequired({
|
|
25
28
|
executionMode: input.executionMode,
|
|
@@ -33,7 +36,9 @@ export function createWorkspaceContext(input) {
|
|
|
33
36
|
executionMode: input.executionMode,
|
|
34
37
|
toolName: input.toolName,
|
|
35
38
|
runId: input.runId?.trim() || randomUUID(),
|
|
36
|
-
workspaceResolution:
|
|
39
|
+
workspaceResolution: lockedWorkspaceId && !explicitWorkspaceId
|
|
40
|
+
? "locked_profile"
|
|
41
|
+
: "explicit",
|
|
37
42
|
});
|
|
38
43
|
return { ok: true, context };
|
|
39
44
|
}
|
|
@@ -4,7 +4,7 @@ import { lstat, mkdir, readFile, rename, rm, writeFile, } from "node:fs/promises
|
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { getApi } from "../api.js";
|
|
7
|
-
import { getConfig } from "../auth.js";
|
|
7
|
+
import { getConfig, getEffectiveConfiguredWorkspaceId } from "../auth.js";
|
|
8
8
|
const MAX_FILTER_IDS = 100;
|
|
9
9
|
const MAX_QUERY_LENGTH = 8000;
|
|
10
10
|
export const workspaceExportToolDefinitions = [
|
|
@@ -340,7 +340,7 @@ async function writeJsonAtomic(filePath, value) {
|
|
|
340
340
|
}
|
|
341
341
|
export async function exportWorkspaceCsv(input = {}) {
|
|
342
342
|
const config = getConfig();
|
|
343
|
-
const workspaceId = config
|
|
343
|
+
const workspaceId = getEffectiveConfiguredWorkspaceId(config);
|
|
344
344
|
if (!workspaceId) {
|
|
345
345
|
throw new Error("No active workspace selected. Run list_workspaces then set_active_workspace before export_workspace_csv.");
|
|
346
346
|
}
|
|
@@ -140,21 +140,56 @@ export declare const workspaceToolDefinitions: ({
|
|
|
140
140
|
})[];
|
|
141
141
|
export declare function listWorkspaces(): Promise<{
|
|
142
142
|
workspaces: WorkspaceSummary[];
|
|
143
|
+
workspaceLock: {
|
|
144
|
+
enabled: boolean;
|
|
145
|
+
workspaceId?: undefined;
|
|
146
|
+
hiddenWorkspaceCount?: undefined;
|
|
147
|
+
};
|
|
148
|
+
} | {
|
|
149
|
+
workspaces: WorkspaceSummary[];
|
|
150
|
+
workspaceLock: {
|
|
151
|
+
enabled: boolean;
|
|
152
|
+
workspaceId: string;
|
|
153
|
+
hiddenWorkspaceCount: number;
|
|
154
|
+
};
|
|
143
155
|
}>;
|
|
144
156
|
export declare function getActiveWorkspace(): {
|
|
145
157
|
activeWorkspaceId: string | null;
|
|
146
158
|
activeWorkspaceName: string | null;
|
|
159
|
+
workspaceLock: {
|
|
160
|
+
enabled: boolean;
|
|
161
|
+
workspaceId: string;
|
|
162
|
+
} | {
|
|
163
|
+
enabled: boolean;
|
|
164
|
+
workspaceId?: undefined;
|
|
165
|
+
};
|
|
147
166
|
};
|
|
148
167
|
export declare function getWorkspace(workspaceId: string): Promise<{
|
|
168
|
+
ok: boolean;
|
|
169
|
+
error: string;
|
|
170
|
+
workspace?: undefined;
|
|
171
|
+
} | {
|
|
149
172
|
workspace: WorkspaceDetails;
|
|
173
|
+
ok?: undefined;
|
|
174
|
+
error?: undefined;
|
|
150
175
|
}>;
|
|
151
176
|
export declare function setActiveWorkspace(workspaceId: string, userConfirmed?: boolean): Promise<{
|
|
152
177
|
ok: boolean;
|
|
178
|
+
code: string;
|
|
179
|
+
activeWorkspaceId: string;
|
|
180
|
+
requestedWorkspaceId: string;
|
|
153
181
|
error: string;
|
|
154
182
|
requiresConfirmation?: undefined;
|
|
155
|
-
activeWorkspaceId?: undefined;
|
|
156
183
|
activeWorkspaceName?: undefined;
|
|
184
|
+
requestedWorkspaceName?: undefined;
|
|
185
|
+
} | {
|
|
186
|
+
ok: boolean;
|
|
187
|
+
error: string;
|
|
188
|
+
code?: undefined;
|
|
189
|
+
activeWorkspaceId?: undefined;
|
|
157
190
|
requestedWorkspaceId?: undefined;
|
|
191
|
+
requiresConfirmation?: undefined;
|
|
192
|
+
activeWorkspaceName?: undefined;
|
|
158
193
|
requestedWorkspaceName?: undefined;
|
|
159
194
|
} | {
|
|
160
195
|
ok: boolean;
|
|
@@ -164,22 +199,33 @@ export declare function setActiveWorkspace(workspaceId: string, userConfirmed?:
|
|
|
164
199
|
requestedWorkspaceId: string;
|
|
165
200
|
requestedWorkspaceName: string;
|
|
166
201
|
error: string;
|
|
202
|
+
code?: undefined;
|
|
167
203
|
} | {
|
|
168
204
|
ok: boolean;
|
|
169
205
|
activeWorkspaceId: string;
|
|
170
206
|
activeWorkspaceName: string;
|
|
207
|
+
code?: undefined;
|
|
208
|
+
requestedWorkspaceId?: undefined;
|
|
171
209
|
error?: undefined;
|
|
172
210
|
requiresConfirmation?: undefined;
|
|
173
|
-
requestedWorkspaceId?: undefined;
|
|
174
211
|
requestedWorkspaceName?: undefined;
|
|
175
212
|
}>;
|
|
176
213
|
export declare function createWorkspace(name: string): Promise<{
|
|
214
|
+
ok: boolean;
|
|
215
|
+
code: string;
|
|
216
|
+
activeWorkspaceId: string;
|
|
217
|
+
error: string;
|
|
218
|
+
workspace?: undefined;
|
|
219
|
+
} | {
|
|
177
220
|
workspace: {
|
|
178
221
|
id: string;
|
|
179
222
|
name: string;
|
|
180
223
|
slug: string;
|
|
181
224
|
};
|
|
182
225
|
activeWorkspaceId: string;
|
|
226
|
+
ok?: undefined;
|
|
227
|
+
code?: undefined;
|
|
228
|
+
error?: undefined;
|
|
183
229
|
}>;
|
|
184
230
|
export declare function addTeammate(input: {
|
|
185
231
|
workspaceId?: string;
|
package/dist/tools/workspaces.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getApi, resetApi } from "../api.js";
|
|
2
|
-
import { getConfig, updateActiveWorkspace } from "../auth.js";
|
|
2
|
+
import { getConfig, getEffectiveConfiguredWorkspaceId, getLockedWorkspaceId, resolveWorkspaceIdForRequest, updateActiveWorkspace, } from "../auth.js";
|
|
3
3
|
export const workspaceToolDefinitions = [
|
|
4
4
|
{
|
|
5
5
|
name: "list_workspaces",
|
|
@@ -81,21 +81,53 @@ export const workspaceToolDefinitions = [
|
|
|
81
81
|
export async function listWorkspaces() {
|
|
82
82
|
const api = getApi();
|
|
83
83
|
const { workspaces } = await api.get("/api/v3/workspaces");
|
|
84
|
-
|
|
84
|
+
const lockedWorkspaceId = getLockedWorkspaceId();
|
|
85
|
+
if (!lockedWorkspaceId) {
|
|
86
|
+
return { workspaces, workspaceLock: { enabled: false } };
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
workspaces: workspaces.filter((ws) => ws.id === lockedWorkspaceId),
|
|
90
|
+
workspaceLock: {
|
|
91
|
+
enabled: true,
|
|
92
|
+
workspaceId: lockedWorkspaceId,
|
|
93
|
+
hiddenWorkspaceCount: workspaces.filter((ws) => ws.id !== lockedWorkspaceId).length,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
85
96
|
}
|
|
86
97
|
export function getActiveWorkspace() {
|
|
87
98
|
const config = getConfig();
|
|
99
|
+
const lockedWorkspaceId = getLockedWorkspaceId();
|
|
88
100
|
return {
|
|
89
|
-
activeWorkspaceId: config
|
|
101
|
+
activeWorkspaceId: getEffectiveConfiguredWorkspaceId(config),
|
|
90
102
|
activeWorkspaceName: config.activeWorkspaceName || null,
|
|
103
|
+
workspaceLock: lockedWorkspaceId
|
|
104
|
+
? { enabled: true, workspaceId: lockedWorkspaceId }
|
|
105
|
+
: { enabled: false },
|
|
91
106
|
};
|
|
92
107
|
}
|
|
93
108
|
export async function getWorkspace(workspaceId) {
|
|
109
|
+
const allowedWorkspaceId = resolveWorkspaceIdForRequest(workspaceId, "get_workspace");
|
|
110
|
+
if (!allowedWorkspaceId) {
|
|
111
|
+
return {
|
|
112
|
+
ok: false,
|
|
113
|
+
error: "No active workspace selected.",
|
|
114
|
+
};
|
|
115
|
+
}
|
|
94
116
|
const api = getApi();
|
|
95
|
-
const { workspace } = await api.get(`/api/v3/workspaces/${encodeURIComponent(
|
|
117
|
+
const { workspace } = await api.get(`/api/v3/workspaces/${encodeURIComponent(allowedWorkspaceId)}`);
|
|
96
118
|
return { workspace };
|
|
97
119
|
}
|
|
98
120
|
export async function setActiveWorkspace(workspaceId, userConfirmed) {
|
|
121
|
+
const lockedWorkspaceId = getLockedWorkspaceId();
|
|
122
|
+
if (lockedWorkspaceId && workspaceId !== lockedWorkspaceId) {
|
|
123
|
+
return {
|
|
124
|
+
ok: false,
|
|
125
|
+
code: "WORKSPACE_LOCKED",
|
|
126
|
+
activeWorkspaceId: lockedWorkspaceId,
|
|
127
|
+
requestedWorkspaceId: workspaceId,
|
|
128
|
+
error: "This Hermes profile is locked to its customer workspace and cannot switch workspaces.",
|
|
129
|
+
};
|
|
130
|
+
}
|
|
99
131
|
const api = getApi();
|
|
100
132
|
const { workspaces } = await api.get("/api/v3/workspaces");
|
|
101
133
|
const match = workspaces.find((ws) => ws.id === workspaceId);
|
|
@@ -131,6 +163,15 @@ export async function setActiveWorkspace(workspaceId, userConfirmed) {
|
|
|
131
163
|
};
|
|
132
164
|
}
|
|
133
165
|
export async function createWorkspace(name) {
|
|
166
|
+
const lockedWorkspaceId = getLockedWorkspaceId();
|
|
167
|
+
if (lockedWorkspaceId) {
|
|
168
|
+
return {
|
|
169
|
+
ok: false,
|
|
170
|
+
code: "WORKSPACE_LOCKED",
|
|
171
|
+
activeWorkspaceId: lockedWorkspaceId,
|
|
172
|
+
error: "This Hermes profile is locked to its customer workspace and cannot create or switch workspaces. Use the sellable-admin profile for provisioning.",
|
|
173
|
+
};
|
|
174
|
+
}
|
|
134
175
|
const api = getApi();
|
|
135
176
|
const { workspace } = await api.post("/api/v3/workspaces", { name });
|
|
136
177
|
updateActiveWorkspace({
|
|
@@ -146,7 +187,9 @@ export async function createWorkspace(name) {
|
|
|
146
187
|
export async function addTeammate(input) {
|
|
147
188
|
const args = input || {};
|
|
148
189
|
const config = getConfig();
|
|
149
|
-
const workspaceId = args.workspaceId ||
|
|
190
|
+
const workspaceId = resolveWorkspaceIdForRequest(args.workspaceId ||
|
|
191
|
+
getEffectiveConfiguredWorkspaceId(config) ||
|
|
192
|
+
undefined, "add_teammate");
|
|
150
193
|
if (!workspaceId) {
|
|
151
194
|
return {
|
|
152
195
|
ok: false,
|
package/package.json
CHANGED
|
@@ -59,11 +59,6 @@ The tail MUST verify:
|
|
|
59
59
|
|
|
60
60
|
- `attach_recommended_sequence` is the only way to pick a template in
|
|
61
61
|
the autonomous tail. Do NOT hand-wire a sequence at Step 16.
|
|
62
|
-
- Exception: when the parent `$sellable:create-evergreen-campaigns` plan
|
|
63
|
-
explicitly carries `campaignSequenceOptions:{ mode:"connection_only" }`,
|
|
64
|
-
do not call `attach_recommended_sequence`. Use the backend-owned
|
|
65
|
-
`attach_sequence({ tableId, templateRef:"connection_only", currentStep:"send" })` path and prove
|
|
66
|
-
`sequenceReceipt.actionTypes:["send_invite"]`.
|
|
67
62
|
- Tier mismatch at attach time is an escalation, NOT a silent fallback.
|
|
68
63
|
- A Standard sender MUST NOT be attached to an INMAIL_OPEN template.
|
|
69
64
|
This would produce sends that fail at Unipile with `not_authorized`.
|