@memberjunction/core-actions 6.1.2 → 6.2.0-edge.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/config.d.ts +68 -10
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +32 -5
  4. package/dist/config.js.map +1 -1
  5. package/dist/custom/ai/generate-image.action.d.ts +13 -1
  6. package/dist/custom/ai/generate-image.action.d.ts.map +1 -1
  7. package/dist/custom/ai/generate-image.action.js +25 -13
  8. package/dist/custom/ai/generate-image.action.js.map +1 -1
  9. package/dist/custom/utilities/base-file-handler.d.ts.map +1 -1
  10. package/dist/custom/utilities/base-file-handler.js +4 -2
  11. package/dist/custom/utilities/base-file-handler.js.map +1 -1
  12. package/dist/custom/web/brave-search.action.d.ts +108 -0
  13. package/dist/custom/web/brave-search.action.d.ts.map +1 -0
  14. package/dist/custom/web/brave-search.action.js +285 -0
  15. package/dist/custom/web/brave-search.action.js.map +1 -0
  16. package/dist/custom/web/perplexity-search.action.d.ts +76 -44
  17. package/dist/custom/web/perplexity-search.action.d.ts.map +1 -1
  18. package/dist/custom/web/perplexity-search.action.js +228 -157
  19. package/dist/custom/web/perplexity-search.action.js.map +1 -1
  20. package/dist/custom/web/rss-feed-read.action.d.ts.map +1 -1
  21. package/dist/custom/web/rss-feed-read.action.js +4 -1
  22. package/dist/custom/web/rss-feed-read.action.js.map +1 -1
  23. package/dist/custom/web/web-search.action.d.ts +56 -40
  24. package/dist/custom/web/web-search.action.d.ts.map +1 -1
  25. package/dist/custom/web/web-search.action.js +141 -413
  26. package/dist/custom/web/web-search.action.js.map +1 -1
  27. package/dist/index.d.ts +1 -0
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +1 -0
  30. package/dist/index.js.map +1 -1
  31. package/package.json +40 -39
@@ -0,0 +1,285 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var BraveSearchAction_1;
8
+ import { BaseAction } from "@memberjunction/actions";
9
+ import { RegisterClass } from "@memberjunction/global";
10
+ import { HttpGet, IsHttpError } from "@memberjunction/network-utils";
11
+ import { getApiIntegrationsConfig } from "../../config.js";
12
+ /**
13
+ * Action that searches the web through the Brave Search API.
14
+ *
15
+ * Brave matters here for a reason that is structural rather than qualitative: it
16
+ * serves results from **its own index** (40B+ pages) rather than reselling
17
+ * Google's or Bing's. Every SERP-proxy vendor — and every product built on the
18
+ * Google Custom Search JSON API, which is discontinued on 2027-01-01 — depends on
19
+ * a search engine continuing to permit that access. Brave does not, so it fails
20
+ * independently of the rest of this package's web-search actions. That is the
21
+ * whole point of adding it.
22
+ *
23
+ * Its output shape is deliberately `title` / `url` / `snippet`, which is the shape
24
+ * `Google Custom Search` returns today, so callers migrating off that action are
25
+ * making a rename rather than a rewrite.
26
+ *
27
+ * Authentication is the `X-Subscription-Token` header. Brave requires
28
+ * `Accept: application/json` and returns gzip, which `fetch` negotiates itself.
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * // Basic search
33
+ * await runAction({
34
+ * ActionName: 'Brave Search',
35
+ * Params: [{ Name: 'Query', Value: 'association management software trends' }]
36
+ * });
37
+ *
38
+ * // Recent results only, more of them
39
+ * await runAction({
40
+ * ActionName: 'Brave Search',
41
+ * Params: [
42
+ * { Name: 'Query', Value: 'nonprofit membership dues changes' },
43
+ * { Name: 'Freshness', Value: 'pw' },
44
+ * { Name: 'Count', Value: 20 }
45
+ * ]
46
+ * });
47
+ *
48
+ * // Second page, UK results, with the extra per-page snippets
49
+ * await runAction({
50
+ * ActionName: 'Brave Search',
51
+ * Params: [
52
+ * { Name: 'Query', Value: 'charity commission filing deadlines' },
53
+ * { Name: 'Country', Value: 'GB' },
54
+ * { Name: 'Offset', Value: 1 },
55
+ * { Name: 'ExtraSnippets', Value: true }
56
+ * ]
57
+ * });
58
+ * ```
59
+ */
60
+ let BraveSearchAction = class BraveSearchAction extends BaseAction {
61
+ static { BraveSearchAction_1 = this; }
62
+ static { this.ENDPOINT = 'https://api.search.brave.com/res/v1/web/search'; }
63
+ /** Brave's cap on results per request. */
64
+ static { this.MAX_COUNT = 20; }
65
+ static { this.DEFAULT_COUNT = 10; }
66
+ /** Brave's cap on the pagination offset (in pages, not results). */
67
+ static { this.MAX_OFFSET = 9; }
68
+ /** Brave rejects queries longer than this outright. */
69
+ static { this.MAX_QUERY_CHARS = 400; }
70
+ static { this.SAFESEARCH_VALUES = ['off', 'moderate', 'strict']; }
71
+ /** `pd`/`pw`/`pm`/`py` = past day/week/month/year. Brave also accepts a date range. */
72
+ static { this.FRESHNESS_CODES = ['pd', 'pw', 'pm', 'py']; }
73
+ static { this.FRESHNESS_RANGE = /^\d{4}-\d{2}-\d{2}to\d{4}-\d{2}-\d{2}$/; }
74
+ /**
75
+ * Executes the Brave search.
76
+ *
77
+ * @param params - The action parameters containing:
78
+ * - Query: Search query text (required, max 400 characters)
79
+ * - Count: 1-20 results (default 10)
80
+ * - Offset: Page offset 0-9 (default 0) — pages, not results
81
+ * - Country: 2-letter country code for localisation, e.g. 'US', 'GB'
82
+ * - SearchLang: Language code for results, e.g. 'en'
83
+ * - SafeSearch: 'off' | 'moderate' (default) | 'strict'
84
+ * - Freshness: 'pd' | 'pw' | 'pm' | 'py', or 'YYYY-MM-DDtoYYYY-MM-DD'
85
+ * - ExtraSnippets: Return additional snippets per result (default false).
86
+ * Brave gates this on plan tier, so it is off unless asked for
87
+ *
88
+ * @returns Structured web results with titles, URLs and snippets
89
+ */
90
+ async InternalRunAction(params) {
91
+ const query = this.getStringParam(params, 'query');
92
+ if (!query) {
93
+ return this.createErrorResult("Query parameter is required", "MISSING_QUERY");
94
+ }
95
+ // Rejected rather than truncated: silently cutting a query changes what was
96
+ // asked and returns confident results for a different question.
97
+ if (query.length > BraveSearchAction_1.MAX_QUERY_CHARS) {
98
+ return this.createErrorResult(`Query is ${query.length} characters; Brave's limit is ${BraveSearchAction_1.MAX_QUERY_CHARS}`, "QUERY_TOO_LONG");
99
+ }
100
+ const apiKey = getApiIntegrationsConfig().braveApiKey;
101
+ if (!apiKey) {
102
+ return this.createErrorResult("Brave Search API key not found. Set braveApiKey in mj.config.cjs or the BRAVE_SEARCH_API_KEY environment variable", "MISSING_API_KEY");
103
+ }
104
+ const safeSearch = (this.getStringParam(params, 'safesearch') || 'moderate').toLowerCase();
105
+ if (!BraveSearchAction_1.SAFESEARCH_VALUES.includes(safeSearch)) {
106
+ return this.createErrorResult(`SafeSearch must be one of ${BraveSearchAction_1.SAFESEARCH_VALUES.join(', ')} (got '${safeSearch}')`, "INVALID_SAFESEARCH");
107
+ }
108
+ const freshness = this.getStringParam(params, 'freshness')?.toLowerCase();
109
+ if (freshness && !this.isValidFreshness(freshness)) {
110
+ return this.createErrorResult(`Freshness must be one of ${BraveSearchAction_1.FRESHNESS_CODES.join(', ')} or a 'YYYY-MM-DDtoYYYY-MM-DD' range (got '${freshness}')`, "INVALID_FRESHNESS");
111
+ }
112
+ // Clamped rather than rejected, matching Tavily Search: a caller asking for 50
113
+ // results wants as many as possible, and Brave rejects the whole request above its cap.
114
+ const count = this.clamp(this.getNumericParam(params, 'count', BraveSearchAction_1.DEFAULT_COUNT), 1, BraveSearchAction_1.MAX_COUNT);
115
+ const offset = this.clamp(this.getNumericParam(params, 'offset', 0), 0, BraveSearchAction_1.MAX_OFFSET);
116
+ const extraSnippets = this.getBooleanParam(params, 'extrasnippets', false);
117
+ const requestQuery = { q: query, count, offset, safesearch: safeSearch };
118
+ if (freshness) {
119
+ requestQuery.freshness = freshness;
120
+ }
121
+ const country = this.getStringParam(params, 'country');
122
+ if (country) {
123
+ requestQuery.country = country.toUpperCase();
124
+ }
125
+ const searchLang = this.getStringParam(params, 'searchlang');
126
+ if (searchLang) {
127
+ requestQuery.search_lang = searchLang.toLowerCase();
128
+ }
129
+ if (extraSnippets) {
130
+ requestQuery.extra_snippets = 'true';
131
+ }
132
+ try {
133
+ const response = await HttpGet(BraveSearchAction_1.ENDPOINT, {
134
+ Query: requestQuery,
135
+ Headers: {
136
+ 'X-Subscription-Token': apiKey,
137
+ 'Accept': 'application/json'
138
+ },
139
+ Timeout: 15000
140
+ });
141
+ if (!response.Data) {
142
+ return this.createErrorResult("Empty response from Brave Search API", "EMPTY_RESPONSE");
143
+ }
144
+ const results = (response.Data.web?.results ?? []).map((item) => {
145
+ const mapped = {
146
+ title: item.title ?? '',
147
+ url: item.url ?? '',
148
+ snippet: item.description ?? '',
149
+ displayUrl: item.meta_url?.netloc ?? item.meta_url?.hostname ?? '',
150
+ };
151
+ if (item.age) {
152
+ mapped.age = item.age;
153
+ }
154
+ if (item.page_age) {
155
+ mapped.pageAge = item.page_age;
156
+ }
157
+ if (item.extra_snippets && item.extra_snippets.length > 0) {
158
+ mapped.extraSnippets = item.extra_snippets;
159
+ }
160
+ return mapped;
161
+ });
162
+ // Brave rewrites some queries (spelling, synonyms) and reports it here. A caller
163
+ // debugging unexpected results needs to know the engine searched for something else.
164
+ const alteredQuery = response.Data.query?.altered;
165
+ this.addOutputParam(params, 'Results', results);
166
+ this.addOutputParam(params, 'ResultCount', results.length);
167
+ this.addOutputParam(params, 'SearchResultDetails', {
168
+ query,
169
+ alteredQuery,
170
+ count,
171
+ offset,
172
+ safeSearch,
173
+ freshness,
174
+ results,
175
+ });
176
+ // Zero results is a real answer to a narrow query, not a failure — a caller
177
+ // that treats it as one would retry a query that will keep returning nothing.
178
+ return {
179
+ Success: true,
180
+ ResultCode: "SUCCESS",
181
+ Message: results.length === 0
182
+ ? `Brave returned no results for '${query}'.`
183
+ : `Brave returned ${results.length} result(s) for '${query}'.`
184
+ };
185
+ }
186
+ catch (error) {
187
+ // Status 0 is HttpError's "the request never produced a response" — a timeout or a
188
+ // network failure. That is not an API answer, so it falls through to SEARCH_FAILED
189
+ // rather than being dressed up as an API_ERROR with no status.
190
+ if (IsHttpError(error) && error.Status > 0) {
191
+ return this.mapHttpError(error);
192
+ }
193
+ return this.createErrorResult(`Failed to perform Brave search: ${error instanceof Error ? error.message : String(error)}`, "SEARCH_FAILED");
194
+ }
195
+ }
196
+ mapHttpError(error) {
197
+ const status = error.Status;
198
+ const detail = this.describeErrorBody(error.Data) || error.message;
199
+ if (status === 401 || status === 403) {
200
+ return this.createErrorResult(`Brave rejected the API key (HTTP ${status}): ${detail}`, "INVALID_API_KEY");
201
+ }
202
+ if (status === 429) {
203
+ // Brave meters by plan and, since it retired its free tier in early 2026,
204
+ // bills overage against a stored card. A 429 here is a spend signal, not
205
+ // just a pacing one, so it is surfaced distinctly rather than retried.
206
+ return this.createErrorResult(`Brave rate limit or plan quota exceeded: ${detail}`, "RATE_LIMITED");
207
+ }
208
+ if (status === 422) {
209
+ return this.createErrorResult(`Brave rejected the request parameters (HTTP 422): ${detail}`, "INVALID_REQUEST");
210
+ }
211
+ if (status === 400) {
212
+ return this.createErrorResult(`Brave rejected the request (HTTP 400): ${detail}`, "INVALID_REQUEST");
213
+ }
214
+ return this.createErrorResult(`Brave API error: ${detail}`, "API_ERROR");
215
+ }
216
+ /** Pull whatever explanation the error body carries, without assuming a shape. */
217
+ describeErrorBody(data) {
218
+ if (typeof data === 'string')
219
+ return data.slice(0, 500);
220
+ if (data && typeof data === 'object') {
221
+ const record = data;
222
+ // Brave nests its explanation as { error: { detail: '…' } } on 422s.
223
+ const error = record.error;
224
+ if (error && typeof error === 'object') {
225
+ const nested = error;
226
+ for (const key of ['detail', 'message', 'code']) {
227
+ const value = nested[key];
228
+ if (typeof value === 'string' && value.length > 0)
229
+ return value;
230
+ }
231
+ }
232
+ for (const key of ['detail', 'message', 'error']) {
233
+ const value = record[key];
234
+ if (typeof value === 'string' && value.length > 0)
235
+ return value;
236
+ }
237
+ }
238
+ return '';
239
+ }
240
+ isValidFreshness(value) {
241
+ if (BraveSearchAction_1.FRESHNESS_CODES.includes(value)) {
242
+ return true;
243
+ }
244
+ return BraveSearchAction_1.FRESHNESS_RANGE.test(value);
245
+ }
246
+ clamp(value, min, max) {
247
+ return Math.min(Math.max(Math.floor(value), min), max);
248
+ }
249
+ getStringParam(params, paramName) {
250
+ const param = params.Params.find(p => p.Name.trim().toLowerCase() === paramName.toLowerCase());
251
+ if (param?.Value === undefined || param?.Value === null)
252
+ return undefined;
253
+ const value = String(param.Value).trim();
254
+ return value.length > 0 ? value : undefined;
255
+ }
256
+ getBooleanParam(params, paramName, defaultValue) {
257
+ const param = params.Params.find(p => p.Name.trim().toLowerCase() === paramName.toLowerCase());
258
+ if (param?.Value === undefined || param?.Value === null)
259
+ return defaultValue;
260
+ if (typeof param.Value === 'boolean')
261
+ return param.Value;
262
+ return String(param.Value).trim().toLowerCase() === 'true';
263
+ }
264
+ getNumericParam(params, paramName, defaultValue) {
265
+ const param = params.Params.find(p => p.Name.trim().toLowerCase() === paramName.toLowerCase());
266
+ if (param?.Value === undefined || param?.Value === null || param.Value === '')
267
+ return defaultValue;
268
+ const num = Number(param.Value);
269
+ return isNaN(num) ? defaultValue : num;
270
+ }
271
+ addOutputParam(params, name, value) {
272
+ params.Params.push({ Name: name, Type: 'Output', Value: value });
273
+ }
274
+ createErrorResult(message, code) {
275
+ return { Success: false, Message: message, ResultCode: code };
276
+ }
277
+ };
278
+ BraveSearchAction = BraveSearchAction_1 = __decorate([
279
+ RegisterClass(BaseAction, "Brave Search")
280
+ ], BraveSearchAction);
281
+ export { BraveSearchAction };
282
+ export function LoadBraveSearchAction() {
283
+ // Referenced by consumers to keep this registration from being tree-shaken.
284
+ }
285
+ //# sourceMappingURL=brave-search.action.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brave-search.action.js","sourceRoot":"","sources":["../../../src/custom/web/brave-search.action.ts"],"names":[],"mappings":";;;;;;;AACA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAa,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAsCxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEI,IAAM,iBAAiB,GAAvB,MAAM,iBAAkB,SAAQ,UAAU;;aACrB,aAAQ,GAAG,gDAAgD,AAAnD,CAAoD;IACpF,0CAA0C;aAClB,cAAS,GAAG,EAAE,AAAL,CAAM;aACf,kBAAa,GAAG,EAAE,AAAL,CAAM;IAC3C,oEAAoE;aAC5C,eAAU,GAAG,CAAC,AAAJ,CAAK;IACvC,uDAAuD;aAC/B,oBAAe,GAAG,GAAG,AAAN,CAAO;aAEtB,sBAAiB,GAAG,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,CAAU,AAAzC,CAA0C;IACnF,uFAAuF;aAC/D,oBAAe,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,AAApC,CAAqC;aACpD,oBAAe,GAAG,wCAAwC,AAA3C,CAA4C;IAEnF;;;;;;;;;;;;;;;OAeG;IACO,KAAK,CAAC,iBAAiB,CAAC,MAAuB;QACrD,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACnD,IAAI,CAAC,KAAK,EAAE,CAAC;YACT,OAAO,IAAI,CAAC,iBAAiB,CAAC,6BAA6B,EAAE,eAAe,CAAC,CAAC;QAClF,CAAC;QACD,4EAA4E;QAC5E,gEAAgE;QAChE,IAAI,KAAK,CAAC,MAAM,GAAG,mBAAiB,CAAC,eAAe,EAAE,CAAC;YACnD,OAAO,IAAI,CAAC,iBAAiB,CACzB,YAAY,KAAK,CAAC,MAAM,iCAAiC,mBAAiB,CAAC,eAAe,EAAE,EAC5F,gBAAgB,CACnB,CAAC;QACN,CAAC;QAED,MAAM,MAAM,GAAG,wBAAwB,EAAE,CAAC,WAAW,CAAC;QACtD,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,OAAO,IAAI,CAAC,iBAAiB,CACzB,mHAAmH,EACnH,iBAAiB,CACpB,CAAC;QACN,CAAC;QAED,MAAM,UAAU,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,YAAY,CAAC,IAAI,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC;QAC3F,IAAI,CAAC,mBAAiB,CAAC,iBAAiB,CAAC,QAAQ,CAAC,UAAgE,CAAC,EAAE,CAAC;YAClH,OAAO,IAAI,CAAC,iBAAiB,CACzB,6BAA6B,mBAAiB,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,UAAU,IAAI,EACnG,oBAAoB,CACvB,CAAC;QACN,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC;QAC1E,IAAI,SAAS,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,EAAE,CAAC;YACjD,OAAO,IAAI,CAAC,iBAAiB,CACzB,4BAA4B,mBAAiB,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,8CAA8C,SAAS,IAAI,EACnI,mBAAmB,CACtB,CAAC;QACN,CAAC;QAED,+EAA+E;QAC/E,wFAAwF;QACxF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CACpB,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,mBAAiB,CAAC,aAAa,CAAC,EACtE,CAAC,EACD,mBAAiB,CAAC,SAAS,CAC9B,CAAC;QACF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,mBAAiB,CAAC,UAAU,CAAC,CAAC;QACtG,MAAM,aAAa,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,CAAC,CAAC;QAE3E,MAAM,YAAY,GAAoC,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;QAC1G,IAAI,SAAS,EAAE,CAAC;YACZ,YAAY,CAAC,SAAS,GAAG,SAAS,CAAC;QACvC,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACvD,IAAI,OAAO,EAAE,CAAC;YACV,YAAY,CAAC,OAAO,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;QACjD,CAAC;QACD,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAC7D,IAAI,UAAU,EAAE,CAAC;YACb,YAAY,CAAC,WAAW,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;QACxD,CAAC;QACD,IAAI,aAAa,EAAE,CAAC;YAChB,YAAY,CAAC,cAAc,GAAG,MAAM,CAAC;QACzC,CAAC;QAED,IAAI,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAC1B,mBAAiB,CAAC,QAAQ,EAC1B;gBACI,KAAK,EAAE,YAAY;gBACnB,OAAO,EAAE;oBACL,sBAAsB,EAAE,MAAM;oBAC9B,QAAQ,EAAE,kBAAkB;iBAC/B;gBACD,OAAO,EAAE,KAAK;aACjB,CACJ,CAAC;YAEF,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACjB,OAAO,IAAI,CAAC,iBAAiB,CAAC,sCAAsC,EAAE,gBAAgB,CAAC,CAAC;YAC5F,CAAC;YAED,MAAM,OAAO,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAyB,EAAE;gBACnF,MAAM,MAAM,GAA0B;oBAClC,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE;oBACvB,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,EAAE;oBACnB,OAAO,EAAE,IAAI,CAAC,WAAW,IAAI,EAAE;oBAC/B,UAAU,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,IAAI,IAAI,CAAC,QAAQ,EAAE,QAAQ,IAAI,EAAE;iBACrE,CAAC;gBACF,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;oBACX,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;gBAC1B,CAAC;gBACD,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;oBAChB,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC;gBACnC,CAAC;gBACD,IAAI,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACxD,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC;gBAC/C,CAAC;gBACD,OAAO,MAAM,CAAC;YAClB,CAAC,CAAC,CAAC;YAEH,iFAAiF;YACjF,qFAAqF;YACrF,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC;YAElD,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;YAChD,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;YAC3D,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,qBAAqB,EAAE;gBAC/C,KAAK;gBACL,YAAY;gBACZ,KAAK;gBACL,MAAM;gBACN,UAAU;gBACV,SAAS;gBACT,OAAO;aACV,CAAC,CAAC;YAEH,4EAA4E;YAC5E,8EAA8E;YAC9E,OAAO;gBACH,OAAO,EAAE,IAAI;gBACb,UAAU,EAAE,SAAS;gBACrB,OAAO,EAAE,OAAO,CAAC,MAAM,KAAK,CAAC;oBACzB,CAAC,CAAC,kCAAkC,KAAK,IAAI;oBAC7C,CAAC,CAAC,kBAAkB,OAAO,CAAC,MAAM,mBAAmB,KAAK,IAAI;aACrE,CAAC;QACN,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,mFAAmF;YACnF,mFAAmF;YACnF,+DAA+D;YAC/D,IAAI,WAAW,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzC,OAAO,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YACpC,CAAC;YACD,OAAO,IAAI,CAAC,iBAAiB,CACzB,mCAAmC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAC3F,eAAe,CAClB,CAAC;QACN,CAAC;IACL,CAAC;IAEO,YAAY,CAAC,KAAgB;QACjC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC;QAEnE,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;YACnC,OAAO,IAAI,CAAC,iBAAiB,CACzB,oCAAoC,MAAM,MAAM,MAAM,EAAE,EACxD,iBAAiB,CACpB,CAAC;QACN,CAAC;QACD,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;YACjB,0EAA0E;YAC1E,yEAAyE;YACzE,uEAAuE;YACvE,OAAO,IAAI,CAAC,iBAAiB,CACzB,4CAA4C,MAAM,EAAE,EACpD,cAAc,CACjB,CAAC;QACN,CAAC;QACD,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC,iBAAiB,CACzB,qDAAqD,MAAM,EAAE,EAC7D,iBAAiB,CACpB,CAAC;QACN,CAAC;QACD,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC,iBAAiB,CACzB,0CAA0C,MAAM,EAAE,EAClD,iBAAiB,CACpB,CAAC;QACN,CAAC;QACD,OAAO,IAAI,CAAC,iBAAiB,CAAC,oBAAoB,MAAM,EAAE,EAAE,WAAW,CAAC,CAAC;IAC7E,CAAC;IAED,kFAAkF;IAC1E,iBAAiB,CAAC,IAAa;QACnC,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACxD,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YACnC,MAAM,MAAM,GAAG,IAA+B,CAAC;YAC/C,qEAAqE;YACrE,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;YAC3B,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBACrC,MAAM,MAAM,GAAG,KAAgC,CAAC;gBAChD,KAAK,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,CAAC;oBAC9C,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;oBAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;wBAAE,OAAO,KAAK,CAAC;gBACpE,CAAC;YACL,CAAC;YACD,KAAK,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC;gBAC/C,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;oBAAE,OAAO,KAAK,CAAC;YACpE,CAAC;QACL,CAAC;QACD,OAAO,EAAE,CAAC;IACd,CAAC;IAEO,gBAAgB,CAAC,KAAa;QAClC,IAAI,mBAAiB,CAAC,eAAe,CAAC,QAAQ,CAAC,KAAyD,CAAC,EAAE,CAAC;YACxG,OAAO,IAAI,CAAC;QAChB,CAAC;QACD,OAAO,mBAAiB,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzD,CAAC;IAEO,KAAK,CAAC,KAAa,EAAE,GAAW,EAAE,GAAW;QACjD,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IAC3D,CAAC;IAEO,cAAc,CAAC,MAAuB,EAAE,SAAiB;QAC7D,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;QAC/F,IAAI,KAAK,EAAE,KAAK,KAAK,SAAS,IAAI,KAAK,EAAE,KAAK,KAAK,IAAI;YAAE,OAAO,SAAS,CAAC;QAC1E,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;QACzC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAChD,CAAC;IAEO,eAAe,CAAC,MAAuB,EAAE,SAAiB,EAAE,YAAqB;QACrF,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;QAC/F,IAAI,KAAK,EAAE,KAAK,KAAK,SAAS,IAAI,KAAK,EAAE,KAAK,KAAK,IAAI;YAAE,OAAO,YAAY,CAAC;QAC7E,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC,KAAK,CAAC;QACzD,OAAO,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC;IAC/D,CAAC;IAEO,eAAe,CAAC,MAAuB,EAAE,SAAiB,EAAE,YAAoB;QACpF,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;QAC/F,IAAI,KAAK,EAAE,KAAK,KAAK,SAAS,IAAI,KAAK,EAAE,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,KAAK,KAAK,EAAE;YAAE,OAAO,YAAY,CAAC;QACnG,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAChC,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC;IAC3C,CAAC;IAEO,cAAc,CAAC,MAAuB,EAAE,IAAY,EAAE,KAAc;QACxE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACrE,CAAC;IAEO,iBAAiB,CAAC,OAAe,EAAE,IAAY;QACnD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;IAClE,CAAC;;AAxQQ,iBAAiB;IAD7B,aAAa,CAAC,UAAU,EAAE,cAAc,CAAC;GAC7B,iBAAiB,CAyQ7B;;AAED,MAAM,UAAU,qBAAqB;IACjC,4EAA4E;AAChF,CAAC"}
@@ -1,80 +1,112 @@
1
1
  import { ActionResultSimple, RunActionParams } from "@memberjunction/actions-base";
2
2
  import { BaseAction } from "@memberjunction/actions";
3
+ /** One result from Perplexity's `/search` endpoint, normalised to this package's naming. */
4
+ export interface PerplexitySearchResultItem {
5
+ title: string;
6
+ url: string;
7
+ snippet: string;
8
+ /** Publication date when Perplexity resolved one. */
9
+ date?: string;
10
+ /** When Perplexity last saw the page change. */
11
+ lastUpdated?: string;
12
+ }
13
+ /** Which Perplexity product a call routes to. */
14
+ export type PerplexitySearchMode = 'search' | 'answer';
3
15
  /**
4
- * Action that performs AI-powered web search using Perplexity's Search API
5
- * Returns comprehensive search results with citations and related questions
16
+ * Action that queries Perplexity. It fronts **two different products**, selected
17
+ * by `Mode`, because they answer different questions and cost different money.
18
+ *
19
+ * - **`search` (default)** calls `POST /search`, Perplexity's raw search endpoint.
20
+ * It returns ranked `title` / `url` / `snippet` results with no model in the
21
+ * loop: sub-second, flat-priced per query, and deterministic enough to cache.
22
+ * This is the mode to use when something wants *sources*.
23
+ * - **`answer`** calls `POST /chat/completions` against the Sonar models. It
24
+ * returns synthesised prose plus a flat citation list, takes seconds rather
25
+ * than milliseconds, and bills per token. Use it when a caller genuinely wants
26
+ * a written answer rather than results to read.
27
+ *
28
+ * `search` is the default deliberately. Every caller that omitted `Mode` before
29
+ * this parameter existed was paying Sonar generation prices, and waiting for
30
+ * generation latency, to obtain what was usually just a list of links — and
31
+ * `Citations` is still populated in `search` mode, so callers that only read the
32
+ * URL list keep working unchanged.
6
33
  *
7
34
  * @example
8
35
  * ```typescript
9
- * // Basic search with Perplexity
36
+ * // Structured results — fast, flat-priced (default)
10
37
  * await runAction({
11
38
  * ActionName: 'Perplexity Search',
12
- * Params: [{
13
- * Name: 'Query',
14
- * Value: 'latest developments in quantum computing'
15
- * }]
39
+ * Params: [{ Name: 'Query', Value: 'latest developments in quantum computing' }]
16
40
  * });
17
41
  *
18
- * // Search with specific model and parameters
42
+ * // Structured results, recent only, restricted to two domains
19
43
  * await runAction({
20
44
  * ActionName: 'Perplexity Search',
21
- * Params: [{
22
- * Name: 'Query',
23
- * Value: 'climate change research papers 2024'
24
- * }, {
25
- * Name: 'Model',
26
- * Value: 'sonar-pro'
27
- * }, {
28
- * Name: 'ReturnRelatedQuestions',
29
- * Value: true
30
- * }, {
31
- * Name: 'ReturnImages',
32
- * Value: true
33
- * }]
45
+ * Params: [
46
+ * { Name: 'Query', Value: 'IRS Form 990 filing changes' },
47
+ * { Name: 'MaxResults', Value: 15 },
48
+ * { Name: 'SearchRecencyFilter', Value: 'month' },
49
+ * { Name: 'SearchDomainFilter', Value: ['irs.gov', 'councilofnonprofits.org'] }
50
+ * ]
34
51
  * });
35
52
  *
36
- * // Search with domain filtering
53
+ * // A written answer with citations — slower, token-billed
37
54
  * await runAction({
38
55
  * ActionName: 'Perplexity Search',
39
- * Params: [{
40
- * Name: 'Query',
41
- * Value: 'machine learning tutorials'
42
- * }, {
43
- * Name: 'SearchDomainFilter',
44
- * Value: ['github.com', 'arxiv.org', 'medium.com']
45
- * }]
56
+ * Params: [
57
+ * { Name: 'Query', Value: 'summarise the 2026 nonprofit reporting changes' },
58
+ * { Name: 'Mode', Value: 'answer' },
59
+ * { Name: 'Model', Value: 'sonar-pro' }
60
+ * ]
46
61
  * });
47
62
  * ```
48
63
  */
49
64
  export declare class PerplexitySearchAction extends BaseAction {
65
+ private static readonly SEARCH_ENDPOINT;
66
+ private static readonly CHAT_ENDPOINT;
67
+ /** Perplexity's cap on results per web-search request. */
68
+ private static readonly MAX_RESULTS;
69
+ private static readonly DEFAULT_MAX_RESULTS;
70
+ /** Perplexity's cap on the domain allow/deny list. */
71
+ private static readonly MAX_DOMAIN_FILTERS;
72
+ private static readonly RECENCY_VALUES;
50
73
  /**
51
- * Executes the Perplexity AI search
74
+ * Executes the Perplexity call.
52
75
  *
53
76
  * @param params - The action parameters containing:
54
77
  * - Query: Search query text (required)
55
- * - Model: Perplexity model to use (default: 'sonar')
56
- * Options: sonar, sonar-pro, sonar-reasoning-pro, sonar-deep-research
57
- * Note: the legacy llama-3.1-sonar-* identifiers were retired by Perplexity in
58
- * February 2025 and now fail with an invalid-model error.
59
- * - MaxTokens: Maximum tokens in response (default: 1000)
60
- * - Temperature: Sampling temperature 0-2 (default: 0.2)
61
- * - TopP: Nucleus sampling threshold (default: 0.9)
62
- * - ReturnImages: Include images in results (default: false)
63
- * - ReturnRelatedQuestions: Include related questions (default: false)
64
- * - SearchDomainFilter: Array of domains to limit/exclude search (use '-' prefix to exclude)
65
- * - SearchRecencyFilter: Filter by recency - 'day', 'week', 'month', 'year' (optional)
78
+ * - Mode: 'search' (default) for structured results, or 'answer' for Sonar prose
79
+ * - SearchDomainFilter: Restrict to these domains (max 20) — both modes
80
+ * - SearchRecencyFilter: 'hour' | 'day' | 'week' | 'month' | 'year' — both modes
81
+ *
82
+ * `search` mode only:
83
+ * - MaxResults: 1-20 (default 10)
84
+ * - Country: 2-letter country code for localisation
85
+ *
86
+ * `answer` mode only:
87
+ * - Model: 'sonar' (default), 'sonar-pro', 'sonar-reasoning-pro', 'sonar-deep-research'.
88
+ * Note the legacy `llama-3.1-sonar-*` identifiers were retired by Perplexity in
89
+ * February 2025 and now fail with an invalid-model error
90
+ * - MaxTokens, Temperature, TopP
91
+ * - ReturnImages, ReturnRelatedQuestions
66
92
  *
67
- * @returns Search results with content, citations, and related questions
93
+ * @returns Structured results in `search` mode; prose plus citations in `answer` mode
68
94
  */
69
95
  protected InternalRunAction(params: RunActionParams): Promise<ActionResultSimple>;
96
+ /** `POST /search` — structured results, no model in the loop. */
97
+ private runSearchMode;
98
+ /** `POST /chat/completions` — Sonar prose plus citations. */
99
+ private runAnswerMode;
100
+ private mapHttpError;
70
101
  /**
71
102
  * Get string parameter value
72
103
  */
73
104
  private getStringParam;
74
105
  /**
75
- * Get parameter value (any type)
106
+ * A list param, accepted as a real array or as a comma-separated string — both
107
+ * forms arrive in practice, from agent input mappings and from humans respectively.
76
108
  */
77
- private getParamValue;
109
+ private getStringArrayParam;
78
110
  /**
79
111
  * Get boolean parameter value with default
80
112
  */
@@ -1 +1 @@
1
- {"version":3,"file":"perplexity-search.action.d.ts","sourceRoot":"","sources":["../../../src/custom/web/perplexity-search.action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAiBrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,qBACa,sBAAuB,SAAQ,UAAU;IAElD;;;;;;;;;;;;;;;;;;OAkBG;cACa,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA6JvF;;OAEG;IACH,OAAO,CAAC,cAAc;IAMtB;;OAEG;IACH,OAAO,CAAC,aAAa;IAKrB;;OAEG;IACH,OAAO,CAAC,eAAe;IAMvB;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACH,OAAO,CAAC,iBAAiB;CAO5B"}
1
+ {"version":3,"file":"perplexity-search.action.d.ts","sourceRoot":"","sources":["../../../src/custom/web/perplexity-search.action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAiBrD,4FAA4F;AAC5F,MAAM,WAAW,0BAA0B;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAgBD,iDAAiD;AACjD,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,qBACa,sBAAuB,SAAQ,UAAU;IAClD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAsC;IAC7E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAgD;IACrF,0DAA0D;IAC1D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAM;IACzC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAM;IACjD,sDAAsD;IACtD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAM;IAChD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAqD;IAE3F;;;;;;;;;;;;;;;;;;;;;OAqBG;cACa,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAoDvF,iEAAiE;YACnD,aAAa;IA4E3B,6DAA6D;YAC/C,aAAa;IA2E3B,OAAO,CAAC,YAAY;IAoBpB;;OAEG;IACH,OAAO,CAAC,cAAc;IAMtB;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAQ3B;;OAEG;IACH,OAAO,CAAC,eAAe;IAMvB;;OAEG;IACH,OAAO,CAAC,eAAe;IAOvB;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACH,OAAO,CAAC,iBAAiB;CAO5B"}