@ubiquity-os/plugin-sdk 3.3.4 → 3.4.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/dist/index.d.mts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +75 -55
- package/dist/index.mjs +73 -54
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -129,11 +129,13 @@ interface Options$1 {
|
|
|
129
129
|
bypassSignatureVerification?: boolean;
|
|
130
130
|
returnDataToKernel?: boolean;
|
|
131
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Removes wrapper backticks or fenced blocks that LLMs often return around payloads.
|
|
134
|
+
*/
|
|
135
|
+
declare function sanitizeLlmResponse(input: string): string;
|
|
132
136
|
|
|
133
137
|
declare function createActionsPlugin<TConfig = unknown, TEnv = unknown, TCommand = unknown, TSupportedEvents extends EmitterWebhookEventName = EmitterWebhookEventName>(handler: (context: Context<TConfig, TEnv, TCommand, TSupportedEvents>) => HandlerReturn, options?: Options$1): Promise<void>;
|
|
134
138
|
|
|
135
|
-
declare function createPlugin<TConfig = unknown, TEnv = unknown, TCommand = unknown, TSupportedEvents extends EmitterWebhookEventName = EmitterWebhookEventName>(handler: (context: Context<TConfig, TEnv, TCommand, TSupportedEvents>) => HandlerReturn, manifest: Manifest, options?: Options$1): Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
|
|
136
|
-
|
|
137
139
|
/**
|
|
138
140
|
* Options for cleanMarkdown.
|
|
139
141
|
*
|
|
@@ -164,4 +166,6 @@ type Options = {
|
|
|
164
166
|
*/
|
|
165
167
|
declare function cleanMarkdown(md: string, options?: Options): string;
|
|
166
168
|
|
|
167
|
-
|
|
169
|
+
declare function createPlugin<TConfig = unknown, TEnv = unknown, TCommand = unknown, TSupportedEvents extends EmitterWebhookEventName = EmitterWebhookEventName>(handler: (context: Context<TConfig, TEnv, TCommand, TSupportedEvents>) => HandlerReturn, manifest: Manifest, options?: Options$1): Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
|
|
170
|
+
|
|
171
|
+
export { CommentHandler, type Context, type Options$1 as Options, cleanMarkdown, createActionsPlugin, createPlugin, sanitizeLlmResponse };
|
package/dist/index.d.ts
CHANGED
|
@@ -129,11 +129,13 @@ interface Options$1 {
|
|
|
129
129
|
bypassSignatureVerification?: boolean;
|
|
130
130
|
returnDataToKernel?: boolean;
|
|
131
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Removes wrapper backticks or fenced blocks that LLMs often return around payloads.
|
|
134
|
+
*/
|
|
135
|
+
declare function sanitizeLlmResponse(input: string): string;
|
|
132
136
|
|
|
133
137
|
declare function createActionsPlugin<TConfig = unknown, TEnv = unknown, TCommand = unknown, TSupportedEvents extends EmitterWebhookEventName = EmitterWebhookEventName>(handler: (context: Context<TConfig, TEnv, TCommand, TSupportedEvents>) => HandlerReturn, options?: Options$1): Promise<void>;
|
|
134
138
|
|
|
135
|
-
declare function createPlugin<TConfig = unknown, TEnv = unknown, TCommand = unknown, TSupportedEvents extends EmitterWebhookEventName = EmitterWebhookEventName>(handler: (context: Context<TConfig, TEnv, TCommand, TSupportedEvents>) => HandlerReturn, manifest: Manifest, options?: Options$1): Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
|
|
136
|
-
|
|
137
139
|
/**
|
|
138
140
|
* Options for cleanMarkdown.
|
|
139
141
|
*
|
|
@@ -164,4 +166,6 @@ type Options = {
|
|
|
164
166
|
*/
|
|
165
167
|
declare function cleanMarkdown(md: string, options?: Options): string;
|
|
166
168
|
|
|
167
|
-
|
|
169
|
+
declare function createPlugin<TConfig = unknown, TEnv = unknown, TCommand = unknown, TSupportedEvents extends EmitterWebhookEventName = EmitterWebhookEventName>(handler: (context: Context<TConfig, TEnv, TCommand, TSupportedEvents>) => HandlerReturn, manifest: Manifest, options?: Options$1): Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
|
|
170
|
+
|
|
171
|
+
export { CommentHandler, type Context, type Options$1 as Options, cleanMarkdown, createActionsPlugin, createPlugin, sanitizeLlmResponse };
|
package/dist/index.js
CHANGED
|
@@ -33,7 +33,8 @@ __export(src_exports, {
|
|
|
33
33
|
CommentHandler: () => CommentHandler,
|
|
34
34
|
cleanMarkdown: () => cleanMarkdown,
|
|
35
35
|
createActionsPlugin: () => createActionsPlugin,
|
|
36
|
-
createPlugin: () => createPlugin
|
|
36
|
+
createPlugin: () => createPlugin,
|
|
37
|
+
sanitizeLlmResponse: () => sanitizeLlmResponse
|
|
37
38
|
});
|
|
38
39
|
module.exports = __toCommonJS(src_exports);
|
|
39
40
|
|
|
@@ -156,6 +157,24 @@ JQIDAQAB
|
|
|
156
157
|
function sanitizeMetadata(obj) {
|
|
157
158
|
return JSON.stringify(obj, null, 2).replace(/</g, "<").replace(/>/g, ">").replace(/--/g, "--");
|
|
158
159
|
}
|
|
160
|
+
function sanitizeLlmResponse(input) {
|
|
161
|
+
const trimmed = input.trim();
|
|
162
|
+
if (!trimmed) {
|
|
163
|
+
return trimmed;
|
|
164
|
+
}
|
|
165
|
+
if (trimmed.startsWith("```")) {
|
|
166
|
+
let result = trimmed.replace(/^```[a-z0-9+-]*\s*(?:\r\n|\n)?/i, "");
|
|
167
|
+
if (result.endsWith("```")) {
|
|
168
|
+
result = result.slice(0, -3);
|
|
169
|
+
result = result.replace(/[\r\n]+$/, "");
|
|
170
|
+
}
|
|
171
|
+
return result.trim();
|
|
172
|
+
}
|
|
173
|
+
if (trimmed.startsWith("`") && trimmed.endsWith("`")) {
|
|
174
|
+
return trimmed.slice(1, -1).trim();
|
|
175
|
+
}
|
|
176
|
+
return trimmed;
|
|
177
|
+
}
|
|
159
178
|
function getPluginOptions(options) {
|
|
160
179
|
return {
|
|
161
180
|
// Important to use || and not ?? to not consider empty strings
|
|
@@ -553,6 +572,59 @@ async function returnDataToKernel(repoToken, stateId, output) {
|
|
|
553
572
|
});
|
|
554
573
|
}
|
|
555
574
|
|
|
575
|
+
// src/markdown.ts
|
|
576
|
+
var VOID_TAGS = /* @__PURE__ */ new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]);
|
|
577
|
+
function cleanMarkdown(md, options = {}) {
|
|
578
|
+
const codeBlockRegex = /(```[\s\S]*?```|~~~[\s\S]*?~~~)/g;
|
|
579
|
+
const { tags = [], shouldCollapseEmptyLines = false } = options;
|
|
580
|
+
const segments = [];
|
|
581
|
+
let lastIndex = 0;
|
|
582
|
+
const matches = [...md.matchAll(codeBlockRegex)];
|
|
583
|
+
for (const match of matches) {
|
|
584
|
+
if (match.index > lastIndex) {
|
|
585
|
+
segments.push(processSegment(md.slice(lastIndex, match.index), tags, shouldCollapseEmptyLines));
|
|
586
|
+
}
|
|
587
|
+
segments.push(match[0]);
|
|
588
|
+
lastIndex = match.index + match[0].length;
|
|
589
|
+
}
|
|
590
|
+
if (lastIndex < md.length) {
|
|
591
|
+
segments.push(processSegment(md.slice(lastIndex), tags, shouldCollapseEmptyLines));
|
|
592
|
+
}
|
|
593
|
+
return segments.join("");
|
|
594
|
+
}
|
|
595
|
+
function processSegment(segment, extraTags, shouldCollapseEmptyLines) {
|
|
596
|
+
const inlineCodeRegex = /`[^`]*`/g;
|
|
597
|
+
const inlineCodes = [];
|
|
598
|
+
let s = segment.replace(inlineCodeRegex, (m) => {
|
|
599
|
+
inlineCodes.push(m);
|
|
600
|
+
return `__INLINE_CODE_${inlineCodes.length - 1}__`;
|
|
601
|
+
});
|
|
602
|
+
s = s.replace(/<!--[\s\S]*?-->/g, "");
|
|
603
|
+
for (const raw of extraTags) {
|
|
604
|
+
if (!raw) continue;
|
|
605
|
+
const tag = raw.toLowerCase().trim().replace(/[^\w:-]/g, "");
|
|
606
|
+
if (!tag) continue;
|
|
607
|
+
if (VOID_TAGS.has(tag)) {
|
|
608
|
+
const voidRe = new RegExp(`<${tag}\\b[^>]*\\/?>`, "gi");
|
|
609
|
+
s = s.replace(voidRe, "");
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
const pairRe = new RegExp(`<${tag}\\b[^>]*>[\\s\\S]*?<\\/${tag}>`, "gi");
|
|
613
|
+
let prev;
|
|
614
|
+
do {
|
|
615
|
+
prev = s;
|
|
616
|
+
s = s.replace(pairRe, "");
|
|
617
|
+
} while (s !== prev);
|
|
618
|
+
const openCloseRe = new RegExp(`<\\/?${tag}\\b[^>]*>`, "gi");
|
|
619
|
+
s = s.replace(openCloseRe, "");
|
|
620
|
+
}
|
|
621
|
+
s = s.replace(/__INLINE_CODE_(\d+)__/g, (str, idx) => inlineCodes[+idx]);
|
|
622
|
+
if (shouldCollapseEmptyLines) {
|
|
623
|
+
s = s.replace(/[ \t]+$/gm, "").replace(/\n{3,}/g, "\n\n");
|
|
624
|
+
}
|
|
625
|
+
return s;
|
|
626
|
+
}
|
|
627
|
+
|
|
556
628
|
// src/server.ts
|
|
557
629
|
var import_value4 = require("@sinclair/typebox/value");
|
|
558
630
|
var import_ubiquity_os_logger4 = require("@ubiquity-os/ubiquity-os-logger");
|
|
@@ -633,63 +705,11 @@ function createPlugin(handler, manifest, options) {
|
|
|
633
705
|
});
|
|
634
706
|
return app;
|
|
635
707
|
}
|
|
636
|
-
|
|
637
|
-
// src/markdown.ts
|
|
638
|
-
var VOID_TAGS = /* @__PURE__ */ new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]);
|
|
639
|
-
function cleanMarkdown(md, options = {}) {
|
|
640
|
-
const codeBlockRegex = /(```[\s\S]*?```|~~~[\s\S]*?~~~)/g;
|
|
641
|
-
const { tags = [], shouldCollapseEmptyLines = false } = options;
|
|
642
|
-
const segments = [];
|
|
643
|
-
let lastIndex = 0;
|
|
644
|
-
const matches = [...md.matchAll(codeBlockRegex)];
|
|
645
|
-
for (const match of matches) {
|
|
646
|
-
if (match.index > lastIndex) {
|
|
647
|
-
segments.push(processSegment(md.slice(lastIndex, match.index), tags, shouldCollapseEmptyLines));
|
|
648
|
-
}
|
|
649
|
-
segments.push(match[0]);
|
|
650
|
-
lastIndex = match.index + match[0].length;
|
|
651
|
-
}
|
|
652
|
-
if (lastIndex < md.length) {
|
|
653
|
-
segments.push(processSegment(md.slice(lastIndex), tags, shouldCollapseEmptyLines));
|
|
654
|
-
}
|
|
655
|
-
return segments.join("");
|
|
656
|
-
}
|
|
657
|
-
function processSegment(segment, extraTags, shouldCollapseEmptyLines) {
|
|
658
|
-
const inlineCodeRegex = /`[^`]*`/g;
|
|
659
|
-
const inlineCodes = [];
|
|
660
|
-
let s = segment.replace(inlineCodeRegex, (m) => {
|
|
661
|
-
inlineCodes.push(m);
|
|
662
|
-
return `__INLINE_CODE_${inlineCodes.length - 1}__`;
|
|
663
|
-
});
|
|
664
|
-
s = s.replace(/<!--[\s\S]*?-->/g, "");
|
|
665
|
-
for (const raw of extraTags) {
|
|
666
|
-
if (!raw) continue;
|
|
667
|
-
const tag = raw.toLowerCase().trim().replace(/[^\w:-]/g, "");
|
|
668
|
-
if (!tag) continue;
|
|
669
|
-
if (VOID_TAGS.has(tag)) {
|
|
670
|
-
const voidRe = new RegExp(`<${tag}\\b[^>]*\\/?>`, "gi");
|
|
671
|
-
s = s.replace(voidRe, "");
|
|
672
|
-
continue;
|
|
673
|
-
}
|
|
674
|
-
const pairRe = new RegExp(`<${tag}\\b[^>]*>[\\s\\S]*?<\\/${tag}>`, "gi");
|
|
675
|
-
let prev;
|
|
676
|
-
do {
|
|
677
|
-
prev = s;
|
|
678
|
-
s = s.replace(pairRe, "");
|
|
679
|
-
} while (s !== prev);
|
|
680
|
-
const openCloseRe = new RegExp(`<\\/?${tag}\\b[^>]*>`, "gi");
|
|
681
|
-
s = s.replace(openCloseRe, "");
|
|
682
|
-
}
|
|
683
|
-
s = s.replace(/__INLINE_CODE_(\d+)__/g, (str, idx) => inlineCodes[+idx]);
|
|
684
|
-
if (shouldCollapseEmptyLines) {
|
|
685
|
-
s = s.replace(/[ \t]+$/gm, "").replace(/\n{3,}/g, "\n\n");
|
|
686
|
-
}
|
|
687
|
-
return s;
|
|
688
|
-
}
|
|
689
708
|
// Annotate the CommonJS export names for ESM import in node:
|
|
690
709
|
0 && (module.exports = {
|
|
691
710
|
CommentHandler,
|
|
692
711
|
cleanMarkdown,
|
|
693
712
|
createActionsPlugin,
|
|
694
|
-
createPlugin
|
|
713
|
+
createPlugin,
|
|
714
|
+
sanitizeLlmResponse
|
|
695
715
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -117,6 +117,24 @@ JQIDAQAB
|
|
|
117
117
|
function sanitizeMetadata(obj) {
|
|
118
118
|
return JSON.stringify(obj, null, 2).replace(/</g, "<").replace(/>/g, ">").replace(/--/g, "--");
|
|
119
119
|
}
|
|
120
|
+
function sanitizeLlmResponse(input) {
|
|
121
|
+
const trimmed = input.trim();
|
|
122
|
+
if (!trimmed) {
|
|
123
|
+
return trimmed;
|
|
124
|
+
}
|
|
125
|
+
if (trimmed.startsWith("```")) {
|
|
126
|
+
let result = trimmed.replace(/^```[a-z0-9+-]*\s*(?:\r\n|\n)?/i, "");
|
|
127
|
+
if (result.endsWith("```")) {
|
|
128
|
+
result = result.slice(0, -3);
|
|
129
|
+
result = result.replace(/[\r\n]+$/, "");
|
|
130
|
+
}
|
|
131
|
+
return result.trim();
|
|
132
|
+
}
|
|
133
|
+
if (trimmed.startsWith("`") && trimmed.endsWith("`")) {
|
|
134
|
+
return trimmed.slice(1, -1).trim();
|
|
135
|
+
}
|
|
136
|
+
return trimmed;
|
|
137
|
+
}
|
|
120
138
|
function getPluginOptions(options) {
|
|
121
139
|
return {
|
|
122
140
|
// Important to use || and not ?? to not consider empty strings
|
|
@@ -514,6 +532,59 @@ async function returnDataToKernel(repoToken, stateId, output) {
|
|
|
514
532
|
});
|
|
515
533
|
}
|
|
516
534
|
|
|
535
|
+
// src/markdown.ts
|
|
536
|
+
var VOID_TAGS = /* @__PURE__ */ new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]);
|
|
537
|
+
function cleanMarkdown(md, options = {}) {
|
|
538
|
+
const codeBlockRegex = /(```[\s\S]*?```|~~~[\s\S]*?~~~)/g;
|
|
539
|
+
const { tags = [], shouldCollapseEmptyLines = false } = options;
|
|
540
|
+
const segments = [];
|
|
541
|
+
let lastIndex = 0;
|
|
542
|
+
const matches = [...md.matchAll(codeBlockRegex)];
|
|
543
|
+
for (const match of matches) {
|
|
544
|
+
if (match.index > lastIndex) {
|
|
545
|
+
segments.push(processSegment(md.slice(lastIndex, match.index), tags, shouldCollapseEmptyLines));
|
|
546
|
+
}
|
|
547
|
+
segments.push(match[0]);
|
|
548
|
+
lastIndex = match.index + match[0].length;
|
|
549
|
+
}
|
|
550
|
+
if (lastIndex < md.length) {
|
|
551
|
+
segments.push(processSegment(md.slice(lastIndex), tags, shouldCollapseEmptyLines));
|
|
552
|
+
}
|
|
553
|
+
return segments.join("");
|
|
554
|
+
}
|
|
555
|
+
function processSegment(segment, extraTags, shouldCollapseEmptyLines) {
|
|
556
|
+
const inlineCodeRegex = /`[^`]*`/g;
|
|
557
|
+
const inlineCodes = [];
|
|
558
|
+
let s = segment.replace(inlineCodeRegex, (m) => {
|
|
559
|
+
inlineCodes.push(m);
|
|
560
|
+
return `__INLINE_CODE_${inlineCodes.length - 1}__`;
|
|
561
|
+
});
|
|
562
|
+
s = s.replace(/<!--[\s\S]*?-->/g, "");
|
|
563
|
+
for (const raw of extraTags) {
|
|
564
|
+
if (!raw) continue;
|
|
565
|
+
const tag = raw.toLowerCase().trim().replace(/[^\w:-]/g, "");
|
|
566
|
+
if (!tag) continue;
|
|
567
|
+
if (VOID_TAGS.has(tag)) {
|
|
568
|
+
const voidRe = new RegExp(`<${tag}\\b[^>]*\\/?>`, "gi");
|
|
569
|
+
s = s.replace(voidRe, "");
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
const pairRe = new RegExp(`<${tag}\\b[^>]*>[\\s\\S]*?<\\/${tag}>`, "gi");
|
|
573
|
+
let prev;
|
|
574
|
+
do {
|
|
575
|
+
prev = s;
|
|
576
|
+
s = s.replace(pairRe, "");
|
|
577
|
+
} while (s !== prev);
|
|
578
|
+
const openCloseRe = new RegExp(`<\\/?${tag}\\b[^>]*>`, "gi");
|
|
579
|
+
s = s.replace(openCloseRe, "");
|
|
580
|
+
}
|
|
581
|
+
s = s.replace(/__INLINE_CODE_(\d+)__/g, (str, idx) => inlineCodes[+idx]);
|
|
582
|
+
if (shouldCollapseEmptyLines) {
|
|
583
|
+
s = s.replace(/[ \t]+$/gm, "").replace(/\n{3,}/g, "\n\n");
|
|
584
|
+
}
|
|
585
|
+
return s;
|
|
586
|
+
}
|
|
587
|
+
|
|
517
588
|
// src/server.ts
|
|
518
589
|
import { Value as Value4 } from "@sinclair/typebox/value";
|
|
519
590
|
import { Logs as Logs2 } from "@ubiquity-os/ubiquity-os-logger";
|
|
@@ -594,62 +665,10 @@ function createPlugin(handler, manifest, options) {
|
|
|
594
665
|
});
|
|
595
666
|
return app;
|
|
596
667
|
}
|
|
597
|
-
|
|
598
|
-
// src/markdown.ts
|
|
599
|
-
var VOID_TAGS = /* @__PURE__ */ new Set(["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]);
|
|
600
|
-
function cleanMarkdown(md, options = {}) {
|
|
601
|
-
const codeBlockRegex = /(```[\s\S]*?```|~~~[\s\S]*?~~~)/g;
|
|
602
|
-
const { tags = [], shouldCollapseEmptyLines = false } = options;
|
|
603
|
-
const segments = [];
|
|
604
|
-
let lastIndex = 0;
|
|
605
|
-
const matches = [...md.matchAll(codeBlockRegex)];
|
|
606
|
-
for (const match of matches) {
|
|
607
|
-
if (match.index > lastIndex) {
|
|
608
|
-
segments.push(processSegment(md.slice(lastIndex, match.index), tags, shouldCollapseEmptyLines));
|
|
609
|
-
}
|
|
610
|
-
segments.push(match[0]);
|
|
611
|
-
lastIndex = match.index + match[0].length;
|
|
612
|
-
}
|
|
613
|
-
if (lastIndex < md.length) {
|
|
614
|
-
segments.push(processSegment(md.slice(lastIndex), tags, shouldCollapseEmptyLines));
|
|
615
|
-
}
|
|
616
|
-
return segments.join("");
|
|
617
|
-
}
|
|
618
|
-
function processSegment(segment, extraTags, shouldCollapseEmptyLines) {
|
|
619
|
-
const inlineCodeRegex = /`[^`]*`/g;
|
|
620
|
-
const inlineCodes = [];
|
|
621
|
-
let s = segment.replace(inlineCodeRegex, (m) => {
|
|
622
|
-
inlineCodes.push(m);
|
|
623
|
-
return `__INLINE_CODE_${inlineCodes.length - 1}__`;
|
|
624
|
-
});
|
|
625
|
-
s = s.replace(/<!--[\s\S]*?-->/g, "");
|
|
626
|
-
for (const raw of extraTags) {
|
|
627
|
-
if (!raw) continue;
|
|
628
|
-
const tag = raw.toLowerCase().trim().replace(/[^\w:-]/g, "");
|
|
629
|
-
if (!tag) continue;
|
|
630
|
-
if (VOID_TAGS.has(tag)) {
|
|
631
|
-
const voidRe = new RegExp(`<${tag}\\b[^>]*\\/?>`, "gi");
|
|
632
|
-
s = s.replace(voidRe, "");
|
|
633
|
-
continue;
|
|
634
|
-
}
|
|
635
|
-
const pairRe = new RegExp(`<${tag}\\b[^>]*>[\\s\\S]*?<\\/${tag}>`, "gi");
|
|
636
|
-
let prev;
|
|
637
|
-
do {
|
|
638
|
-
prev = s;
|
|
639
|
-
s = s.replace(pairRe, "");
|
|
640
|
-
} while (s !== prev);
|
|
641
|
-
const openCloseRe = new RegExp(`<\\/?${tag}\\b[^>]*>`, "gi");
|
|
642
|
-
s = s.replace(openCloseRe, "");
|
|
643
|
-
}
|
|
644
|
-
s = s.replace(/__INLINE_CODE_(\d+)__/g, (str, idx) => inlineCodes[+idx]);
|
|
645
|
-
if (shouldCollapseEmptyLines) {
|
|
646
|
-
s = s.replace(/[ \t]+$/gm, "").replace(/\n{3,}/g, "\n\n");
|
|
647
|
-
}
|
|
648
|
-
return s;
|
|
649
|
-
}
|
|
650
668
|
export {
|
|
651
669
|
CommentHandler,
|
|
652
670
|
cleanMarkdown,
|
|
653
671
|
createActionsPlugin,
|
|
654
|
-
createPlugin
|
|
672
|
+
createPlugin,
|
|
673
|
+
sanitizeLlmResponse
|
|
655
674
|
};
|