@mgsoftwarebv/mg-dashboard-mcp 7.0.23 → 7.2.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.js +757 -37
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -956,9 +956,9 @@ async function fetchAndFormatRun(conn, proxy, sshExec2, instance, runId) {
|
|
|
956
956
|
return { content: [{ type: "text", text: `Invalid API response:
|
|
957
957
|
${rawJson.substring(0, 500)}` }] };
|
|
958
958
|
}
|
|
959
|
-
let
|
|
960
|
-
if (logs)
|
|
961
|
-
return { content: [{ type: "text", text:
|
|
959
|
+
let text8 = formatRunDetail(run);
|
|
960
|
+
if (logs) text8 += "\n\n--- Logs ---\n" + logs;
|
|
961
|
+
return { content: [{ type: "text", text: text8 }] };
|
|
962
962
|
}
|
|
963
963
|
async function waitForCompletion(conn, proxy, sshExec2, instance, runId, waitSeconds) {
|
|
964
964
|
const pollInterval = 3e3;
|
|
@@ -980,10 +980,10 @@ async function waitForCompletion(conn, proxy, sshExec2, instance, runId, waitSec
|
|
|
980
980
|
continue;
|
|
981
981
|
}
|
|
982
982
|
if (TERMINAL_STATUSES.has(run.status)) {
|
|
983
|
-
let
|
|
983
|
+
let text8 = formatRunDetail(run);
|
|
984
984
|
const logs = await fetchRunLogs(runId, conn, proxy, sshExec2);
|
|
985
|
-
if (logs)
|
|
986
|
-
return { content: [{ type: "text", text:
|
|
985
|
+
if (logs) text8 += "\n\n--- Logs ---\n" + logs;
|
|
986
|
+
return { content: [{ type: "text", text: text8 }] };
|
|
987
987
|
}
|
|
988
988
|
}
|
|
989
989
|
return {
|
|
@@ -3670,10 +3670,10 @@ var ZodObject = class _ZodObject extends ZodType {
|
|
|
3670
3670
|
// }) as any;
|
|
3671
3671
|
// return merged;
|
|
3672
3672
|
// }
|
|
3673
|
-
catchall(
|
|
3673
|
+
catchall(index6) {
|
|
3674
3674
|
return new _ZodObject({
|
|
3675
3675
|
...this._def,
|
|
3676
|
-
catchall:
|
|
3676
|
+
catchall: index6
|
|
3677
3677
|
});
|
|
3678
3678
|
}
|
|
3679
3679
|
pick(mask) {
|
|
@@ -3991,9 +3991,9 @@ function mergeValues(a, b) {
|
|
|
3991
3991
|
return { valid: false };
|
|
3992
3992
|
}
|
|
3993
3993
|
const newArray = [];
|
|
3994
|
-
for (let
|
|
3995
|
-
const itemA = a[
|
|
3996
|
-
const itemB = b[
|
|
3994
|
+
for (let index6 = 0; index6 < a.length; index6++) {
|
|
3995
|
+
const itemA = a[index6];
|
|
3996
|
+
const itemB = b[index6];
|
|
3997
3997
|
const sharedValue = mergeValues(itemA, itemB);
|
|
3998
3998
|
if (!sharedValue.valid) {
|
|
3999
3999
|
return { valid: false };
|
|
@@ -4199,10 +4199,10 @@ var ZodMap = class extends ZodType {
|
|
|
4199
4199
|
}
|
|
4200
4200
|
const keyType = this._def.keyType;
|
|
4201
4201
|
const valueType = this._def.valueType;
|
|
4202
|
-
const pairs = [...ctx.data.entries()].map(([key, value],
|
|
4202
|
+
const pairs = [...ctx.data.entries()].map(([key, value], index6) => {
|
|
4203
4203
|
return {
|
|
4204
|
-
key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [
|
|
4205
|
-
value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [
|
|
4204
|
+
key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index6, "key"])),
|
|
4205
|
+
value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index6, "value"]))
|
|
4206
4206
|
};
|
|
4207
4207
|
});
|
|
4208
4208
|
if (ctx.common.async) {
|
|
@@ -5170,6 +5170,222 @@ pgTable("two_factor", {
|
|
|
5170
5170
|
secret: text("secret").notNull(),
|
|
5171
5171
|
backupCodes: text("backup_codes")
|
|
5172
5172
|
});
|
|
5173
|
+
var contentSourceType = pgEnum("content_source_type", [
|
|
5174
|
+
"own_site",
|
|
5175
|
+
"client_site",
|
|
5176
|
+
"external_source",
|
|
5177
|
+
"competitor",
|
|
5178
|
+
"news",
|
|
5179
|
+
"government",
|
|
5180
|
+
"supplier",
|
|
5181
|
+
"unknown"
|
|
5182
|
+
]);
|
|
5183
|
+
var contentSnapshotKind = pgEnum("content_snapshot_kind", [
|
|
5184
|
+
"corpus",
|
|
5185
|
+
"source"
|
|
5186
|
+
]);
|
|
5187
|
+
var contentSources = pgTable(
|
|
5188
|
+
"content_sources",
|
|
5189
|
+
{
|
|
5190
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5191
|
+
url: text("url").notNull(),
|
|
5192
|
+
canonicalUrl: text("canonical_url"),
|
|
5193
|
+
domain: text("domain").notNull(),
|
|
5194
|
+
sourceType: contentSourceType("source_type").notNull().default("unknown"),
|
|
5195
|
+
title: text("title"),
|
|
5196
|
+
author: text("author"),
|
|
5197
|
+
publishedAt: timestamp("published_at", { withTimezone: true }),
|
|
5198
|
+
extractionMethod: text("extraction_method"),
|
|
5199
|
+
summary: text("summary"),
|
|
5200
|
+
facts: jsonb("facts").$type().notNull().default([]),
|
|
5201
|
+
limitations: jsonb("limitations").$type().notNull().default([]),
|
|
5202
|
+
fullText: text("full_text"),
|
|
5203
|
+
textHash: text("text_hash"),
|
|
5204
|
+
wordCount: integer("word_count"),
|
|
5205
|
+
language: text("language"),
|
|
5206
|
+
projectKey: text("project_key"),
|
|
5207
|
+
siteKey: text("site_key"),
|
|
5208
|
+
ticketNumber: text("ticket_number"),
|
|
5209
|
+
metadata: jsonb("metadata").$type().notNull().default({}),
|
|
5210
|
+
fetchedAt: timestamp("fetched_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5211
|
+
lastSeenAt: timestamp("last_seen_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5212
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5213
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5214
|
+
},
|
|
5215
|
+
(table) => [
|
|
5216
|
+
uniqueIndex("content_sources_canonical_url_uidx").on(table.canonicalUrl),
|
|
5217
|
+
index("content_sources_domain_idx").on(table.domain),
|
|
5218
|
+
index("content_sources_type_idx").on(table.sourceType),
|
|
5219
|
+
index("content_sources_text_hash_idx").on(table.textHash),
|
|
5220
|
+
index("content_sources_project_idx").on(table.projectKey),
|
|
5221
|
+
index("content_sources_ticket_idx").on(table.ticketNumber),
|
|
5222
|
+
index("content_sources_last_seen_idx").on(table.lastSeenAt.desc())
|
|
5223
|
+
]
|
|
5224
|
+
);
|
|
5225
|
+
var contentEvidencePacks = pgTable(
|
|
5226
|
+
"content_evidence_packs",
|
|
5227
|
+
{
|
|
5228
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5229
|
+
topic: text("topic"),
|
|
5230
|
+
locale: text("locale"),
|
|
5231
|
+
projectKey: text("project_key"),
|
|
5232
|
+
siteKey: text("site_key"),
|
|
5233
|
+
ticketNumber: text("ticket_number"),
|
|
5234
|
+
generatedAt: timestamp("generated_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5235
|
+
sources: jsonb("sources").$type().notNull().default([]),
|
|
5236
|
+
verifiedFacts: jsonb("verified_facts").$type().notNull().default([]),
|
|
5237
|
+
conflicts: jsonb("conflicts").$type().notNull().default([]),
|
|
5238
|
+
uniqueClaims: jsonb("unique_claims").$type().notNull().default([]),
|
|
5239
|
+
openQuestions: jsonb("open_questions").$type().notNull().default([]),
|
|
5240
|
+
verbatimWarnings: jsonb("verbatim_warnings").$type().notNull().default([]),
|
|
5241
|
+
limitations: jsonb("limitations").$type().notNull().default([]),
|
|
5242
|
+
metadata: jsonb("metadata").$type().notNull().default({}),
|
|
5243
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5244
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5245
|
+
},
|
|
5246
|
+
(table) => [
|
|
5247
|
+
index("content_evidence_packs_project_idx").on(table.projectKey),
|
|
5248
|
+
index("content_evidence_packs_ticket_idx").on(table.ticketNumber),
|
|
5249
|
+
index("content_evidence_packs_generated_idx").on(table.generatedAt.desc())
|
|
5250
|
+
]
|
|
5251
|
+
);
|
|
5252
|
+
pgTable(
|
|
5253
|
+
"content_evidence_pack_source",
|
|
5254
|
+
{
|
|
5255
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5256
|
+
packId: uuid("pack_id").notNull().references(() => contentEvidencePacks.id, { onDelete: "cascade" }),
|
|
5257
|
+
contentSourceId: uuid("content_source_id").notNull().references(() => contentSources.id, { onDelete: "cascade" }),
|
|
5258
|
+
packSourceRef: text("pack_source_ref"),
|
|
5259
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
5260
|
+
},
|
|
5261
|
+
(table) => [
|
|
5262
|
+
uniqueIndex("content_evidence_pack_source_uidx").on(
|
|
5263
|
+
table.packId,
|
|
5264
|
+
table.contentSourceId
|
|
5265
|
+
),
|
|
5266
|
+
index("content_evidence_pack_source_source_idx").on(table.contentSourceId)
|
|
5267
|
+
]
|
|
5268
|
+
);
|
|
5269
|
+
var contentCorpusItems = pgTable(
|
|
5270
|
+
"content_corpus_items",
|
|
5271
|
+
{
|
|
5272
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5273
|
+
projectKey: text("project_key").notNull(),
|
|
5274
|
+
siteKey: text("site_key"),
|
|
5275
|
+
url: text("url").notNull(),
|
|
5276
|
+
canonicalUrl: text("canonical_url"),
|
|
5277
|
+
domain: text("domain").notNull(),
|
|
5278
|
+
pagePath: text("page_path"),
|
|
5279
|
+
sourceType: contentSourceType("source_type").notNull().default("own_site"),
|
|
5280
|
+
title: text("title"),
|
|
5281
|
+
h1: text("h1"),
|
|
5282
|
+
headings: jsonb("headings").$type().notNull().default([]),
|
|
5283
|
+
wordCount: integer("word_count"),
|
|
5284
|
+
fullText: text("full_text"),
|
|
5285
|
+
textHash: text("text_hash"),
|
|
5286
|
+
internalLinks: jsonb("internal_links").$type().notNull().default([]),
|
|
5287
|
+
externalLinks: jsonb("external_links").$type().notNull().default([]),
|
|
5288
|
+
language: text("language"),
|
|
5289
|
+
ticketNumber: text("ticket_number"),
|
|
5290
|
+
metadata: jsonb("metadata").$type().notNull().default({}),
|
|
5291
|
+
extractedAt: timestamp("extracted_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5292
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5293
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
5294
|
+
},
|
|
5295
|
+
(table) => [
|
|
5296
|
+
uniqueIndex("content_corpus_items_project_canonical_uidx").on(
|
|
5297
|
+
table.projectKey,
|
|
5298
|
+
table.canonicalUrl
|
|
5299
|
+
),
|
|
5300
|
+
index("content_corpus_items_domain_idx").on(table.domain),
|
|
5301
|
+
index("content_corpus_items_project_idx").on(table.projectKey),
|
|
5302
|
+
index("content_corpus_items_type_idx").on(table.sourceType),
|
|
5303
|
+
index("content_corpus_items_text_hash_idx").on(table.textHash),
|
|
5304
|
+
index("content_corpus_items_extracted_idx").on(table.extractedAt.desc())
|
|
5305
|
+
]
|
|
5306
|
+
);
|
|
5307
|
+
pgTable(
|
|
5308
|
+
"content_quality_runs",
|
|
5309
|
+
{
|
|
5310
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5311
|
+
corpusItemId: uuid("corpus_item_id").references(
|
|
5312
|
+
() => contentCorpusItems.id,
|
|
5313
|
+
{ onDelete: "set null" }
|
|
5314
|
+
),
|
|
5315
|
+
contentSourceId: uuid("content_source_id").references(
|
|
5316
|
+
() => contentSources.id,
|
|
5317
|
+
{ onDelete: "set null" }
|
|
5318
|
+
),
|
|
5319
|
+
projectKey: text("project_key"),
|
|
5320
|
+
siteKey: text("site_key"),
|
|
5321
|
+
url: text("url"),
|
|
5322
|
+
canonicalUrl: text("canonical_url"),
|
|
5323
|
+
tool: text("tool").notNull(),
|
|
5324
|
+
toolVersion: text("tool_version"),
|
|
5325
|
+
score: integer("score"),
|
|
5326
|
+
flags: jsonb("flags").$type().notNull().default([]),
|
|
5327
|
+
report: jsonb("report").$type().notNull().default({}),
|
|
5328
|
+
reportUrl: text("report_url"),
|
|
5329
|
+
ticketNumber: text("ticket_number"),
|
|
5330
|
+
runAt: timestamp("run_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5331
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
5332
|
+
},
|
|
5333
|
+
(table) => [
|
|
5334
|
+
index("content_quality_runs_corpus_idx").on(table.corpusItemId),
|
|
5335
|
+
index("content_quality_runs_source_idx").on(table.contentSourceId),
|
|
5336
|
+
index("content_quality_runs_project_run_idx").on(
|
|
5337
|
+
table.projectKey,
|
|
5338
|
+
table.runAt.desc()
|
|
5339
|
+
),
|
|
5340
|
+
index("content_quality_runs_url_idx").on(table.url)
|
|
5341
|
+
]
|
|
5342
|
+
);
|
|
5343
|
+
pgTable(
|
|
5344
|
+
"content_snapshots",
|
|
5345
|
+
{
|
|
5346
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
5347
|
+
corpusItemId: uuid("corpus_item_id").references(
|
|
5348
|
+
() => contentCorpusItems.id,
|
|
5349
|
+
{ onDelete: "cascade" }
|
|
5350
|
+
),
|
|
5351
|
+
contentSourceId: uuid("content_source_id").references(
|
|
5352
|
+
() => contentSources.id,
|
|
5353
|
+
{ onDelete: "cascade" }
|
|
5354
|
+
),
|
|
5355
|
+
kind: contentSnapshotKind("kind").notNull(),
|
|
5356
|
+
url: text("url"),
|
|
5357
|
+
canonicalUrl: text("canonical_url"),
|
|
5358
|
+
domain: text("domain"),
|
|
5359
|
+
title: text("title"),
|
|
5360
|
+
fullText: text("full_text"),
|
|
5361
|
+
textHash: text("text_hash"),
|
|
5362
|
+
wordCount: integer("word_count"),
|
|
5363
|
+
headings: jsonb("headings").$type().notNull().default([]),
|
|
5364
|
+
internalLinks: jsonb("internal_links").$type().notNull().default([]),
|
|
5365
|
+
metadata: jsonb("metadata").$type().notNull().default({}),
|
|
5366
|
+
capturedAt: timestamp("captured_at", { withTimezone: true }).notNull().defaultNow(),
|
|
5367
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow()
|
|
5368
|
+
},
|
|
5369
|
+
(table) => [
|
|
5370
|
+
index("content_snapshots_corpus_idx").on(
|
|
5371
|
+
table.corpusItemId,
|
|
5372
|
+
table.capturedAt.desc()
|
|
5373
|
+
),
|
|
5374
|
+
index("content_snapshots_source_idx").on(
|
|
5375
|
+
table.contentSourceId,
|
|
5376
|
+
table.capturedAt.desc()
|
|
5377
|
+
),
|
|
5378
|
+
index("content_snapshots_text_hash_idx").on(table.textHash),
|
|
5379
|
+
uniqueIndex("content_snapshots_corpus_hash_uidx").on(
|
|
5380
|
+
table.corpusItemId,
|
|
5381
|
+
table.textHash
|
|
5382
|
+
),
|
|
5383
|
+
uniqueIndex("content_snapshots_source_hash_uidx").on(
|
|
5384
|
+
table.contentSourceId,
|
|
5385
|
+
table.textHash
|
|
5386
|
+
)
|
|
5387
|
+
]
|
|
5388
|
+
);
|
|
5173
5389
|
var managedServerOs = pgEnum("managed_server_os", [
|
|
5174
5390
|
"linux",
|
|
5175
5391
|
"windows",
|
|
@@ -7075,7 +7291,7 @@ function getEncryptionKey() {
|
|
|
7075
7291
|
throw new Error("ENCRYPTION_KEY must be a 64-character hex string");
|
|
7076
7292
|
return buf;
|
|
7077
7293
|
}
|
|
7078
|
-
function encrypt(
|
|
7294
|
+
function encrypt(text8) {
|
|
7079
7295
|
const key = getEncryptionKey();
|
|
7080
7296
|
const iv = randomBytes(ENC_IV_LENGTH);
|
|
7081
7297
|
const cipher = createCipheriv(
|
|
@@ -7083,7 +7299,7 @@ function encrypt(text7) {
|
|
|
7083
7299
|
new Uint8Array(key),
|
|
7084
7300
|
new Uint8Array(iv)
|
|
7085
7301
|
);
|
|
7086
|
-
let encrypted = cipher.update(
|
|
7302
|
+
let encrypted = cipher.update(text8, "utf8", "hex");
|
|
7087
7303
|
encrypted += cipher.final("hex");
|
|
7088
7304
|
const authTag = cipher.getAuthTag();
|
|
7089
7305
|
return Buffer.concat([
|
|
@@ -7803,10 +8019,10 @@ async function r2GetObjectRange(bucket, key, range) {
|
|
|
7803
8019
|
const body = result.Body;
|
|
7804
8020
|
if (!body?.transformToString)
|
|
7805
8021
|
throw new Error("R2 returned no readable body");
|
|
7806
|
-
const
|
|
8022
|
+
const text8 = await body.transformToString();
|
|
7807
8023
|
const header = `# range: bytes ${range.offset}-${end} of ${size} (${effectiveLen} bytes)`;
|
|
7808
8024
|
return `${header}
|
|
7809
|
-
${
|
|
8025
|
+
${text8}`;
|
|
7810
8026
|
} catch (e) {
|
|
7811
8027
|
throw r2WrapError(bucket, key, e);
|
|
7812
8028
|
}
|
|
@@ -8169,15 +8385,15 @@ async function sftpRead(opts, filePath, proxy, options) {
|
|
|
8169
8385
|
clearTimeout(timer);
|
|
8170
8386
|
cleanup?.();
|
|
8171
8387
|
cleanup = void 0;
|
|
8172
|
-
const
|
|
8388
|
+
const text8 = Buffer.concat(
|
|
8173
8389
|
chunks.map((ch) => new Uint8Array(ch))
|
|
8174
8390
|
).toString("utf-8");
|
|
8175
8391
|
if (!isWholeFileRequest) {
|
|
8176
8392
|
const header = `# range: bytes ${offset}-${offset + effectiveLen - 1} of ${total} (${effectiveLen} bytes)`;
|
|
8177
8393
|
resolve(`${header}
|
|
8178
|
-
${
|
|
8394
|
+
${text8}`);
|
|
8179
8395
|
} else {
|
|
8180
|
-
resolve(
|
|
8396
|
+
resolve(text8);
|
|
8181
8397
|
}
|
|
8182
8398
|
});
|
|
8183
8399
|
rs.on("error", (e) => {
|
|
@@ -8208,7 +8424,25 @@ function formatBytes(bytes) {
|
|
|
8208
8424
|
}
|
|
8209
8425
|
var RESPONSE_MAX_BYTES = 8192;
|
|
8210
8426
|
var NO_FOOTER_TOOLS = /* @__PURE__ */ new Set();
|
|
8211
|
-
var RAW_JSON_TOOLS = /* @__PURE__ */ new Set([
|
|
8427
|
+
var RAW_JSON_TOOLS = /* @__PURE__ */ new Set([
|
|
8428
|
+
"get_mg_dashboard_commits",
|
|
8429
|
+
"extract_article",
|
|
8430
|
+
"research_topic",
|
|
8431
|
+
// Content registry / corpus (2026-DASMG-041) — compact JSON consumed verbatim.
|
|
8432
|
+
"save_content_source",
|
|
8433
|
+
"get_content_source_by_url",
|
|
8434
|
+
"get_content_source_by_id",
|
|
8435
|
+
"search_content_sources",
|
|
8436
|
+
"save_research_pack",
|
|
8437
|
+
"get_research_pack",
|
|
8438
|
+
"save_content_corpus_item",
|
|
8439
|
+
"list_project_content_corpus",
|
|
8440
|
+
"get_content_context_for_project",
|
|
8441
|
+
"save_content_quality_run",
|
|
8442
|
+
"list_content_snapshots",
|
|
8443
|
+
"link_content_pack_to_ticket",
|
|
8444
|
+
"prune_content_snapshots"
|
|
8445
|
+
]);
|
|
8212
8446
|
var TOOL_CACHE_TTL_MS = {
|
|
8213
8447
|
"list-servers": 6e4,
|
|
8214
8448
|
"docker-list": 3e4
|
|
@@ -8308,11 +8542,11 @@ function getKnownContainers(serverId, maxAgeMs = 5 * 6e4) {
|
|
|
8308
8542
|
if (Date.now() - e.capturedAt > maxAgeMs) return void 0;
|
|
8309
8543
|
return e.names;
|
|
8310
8544
|
}
|
|
8311
|
-
function truncateForLLM(
|
|
8312
|
-
const totalBytes = Buffer.byteLength(
|
|
8545
|
+
function truncateForLLM(text8, maxBytes) {
|
|
8546
|
+
const totalBytes = Buffer.byteLength(text8, "utf8");
|
|
8313
8547
|
if (totalBytes <= maxBytes)
|
|
8314
|
-
return { text:
|
|
8315
|
-
const buf = Buffer.from(
|
|
8548
|
+
return { text: text8, truncated: false, totalBytes, shownBytes: totalBytes };
|
|
8549
|
+
const buf = Buffer.from(text8, "utf8");
|
|
8316
8550
|
let cut = maxBytes;
|
|
8317
8551
|
while (cut > 0 && (buf[cut] & 192) === 128) cut--;
|
|
8318
8552
|
const head = buf.subarray(0, cut).toString("utf8");
|
|
@@ -8343,10 +8577,10 @@ function postprocessResult(result, meta) {
|
|
|
8343
8577
|
if (!result.content?.length) return result;
|
|
8344
8578
|
if (RAW_JSON_TOOLS.has(meta.toolName)) return result;
|
|
8345
8579
|
const block = result.content[0];
|
|
8346
|
-
let
|
|
8347
|
-
const trunc = truncateForLLM(
|
|
8580
|
+
let text8 = String(block.text ?? "");
|
|
8581
|
+
const trunc = truncateForLLM(text8, RESPONSE_MAX_BYTES);
|
|
8348
8582
|
if (trunc.truncated) {
|
|
8349
|
-
|
|
8583
|
+
text8 = trunc.text + "\n\n... " + buildTruncationHint(
|
|
8350
8584
|
meta.toolName,
|
|
8351
8585
|
meta.args,
|
|
8352
8586
|
trunc.totalBytes,
|
|
@@ -8360,11 +8594,11 @@ function postprocessResult(result, meta) {
|
|
|
8360
8594
|
const parts = [`took ${tookStr}`, sizeStr];
|
|
8361
8595
|
if (meta.serverIdLabel) parts.push(`server: ${meta.serverIdLabel}`);
|
|
8362
8596
|
if (meta.cached) parts.push("cached");
|
|
8363
|
-
|
|
8597
|
+
text8 = `${text8}
|
|
8364
8598
|
|
|
8365
8599
|
[${parts.join(", ")}]`;
|
|
8366
8600
|
}
|
|
8367
|
-
return { ...result, content: [{ ...block, text:
|
|
8601
|
+
return { ...result, content: [{ ...block, text: text8 }] };
|
|
8368
8602
|
}
|
|
8369
8603
|
function buildPipelineScript(commands, shell, marker, stopOnError) {
|
|
8370
8604
|
if (shell === "powershell") {
|
|
@@ -10356,6 +10590,377 @@ var TOOLS = [
|
|
|
10356
10590
|
required: ["actor", "dateFrom", "dateTo"]
|
|
10357
10591
|
}
|
|
10358
10592
|
},
|
|
10593
|
+
{
|
|
10594
|
+
name: "extract_article",
|
|
10595
|
+
description: "Extract readable article/content data from ONE PUBLIC URL, with a browser-render fallback for pages that block a normal fetch (403) or only render with JavaScript (ticket 2026-DASMG-039). Returns title, author, publishedAt, source, summary, facts[], optional rawText (capped), canonicalUrl, the `extractionMethod` used (http | browser | jsonld | rss | amp | metadata_only) and an explicit `limitations` array (e.g. blocked_without_browser, paywall_detected, partial_content, rawtext_truncated). PUBLIC URLs only \u2014 SSRF-guarded (private/loopback/metadata addresses rejected), no paywall bypass, no credentials; the source/canonical URL is always preserved. Use it to turn a news/article link into safe factual source material for agents; it never auto-publishes or rewrites. For several sources + cross-checking, use research_topic.",
|
|
10596
|
+
inputSchema: {
|
|
10597
|
+
type: "object",
|
|
10598
|
+
properties: {
|
|
10599
|
+
url: {
|
|
10600
|
+
type: "string",
|
|
10601
|
+
description: "Public http(s) URL of the article/page to extract."
|
|
10602
|
+
},
|
|
10603
|
+
includeRawText: {
|
|
10604
|
+
type: "boolean",
|
|
10605
|
+
description: "Include the readable body as `rawText` (default true). Set false for a compact metadata-only result."
|
|
10606
|
+
},
|
|
10607
|
+
maxRawTextChars: {
|
|
10608
|
+
type: "number",
|
|
10609
|
+
description: "Hard cap for `rawText` length (default 20000, max 100000). Truncation adds a `rawtext_truncated` limitation."
|
|
10610
|
+
}
|
|
10611
|
+
},
|
|
10612
|
+
required: ["url"]
|
|
10613
|
+
}
|
|
10614
|
+
},
|
|
10615
|
+
{
|
|
10616
|
+
name: "research_topic",
|
|
10617
|
+
description: "Turn SEVERAL public sources on one topic into a single cross-checked, sourced 'evidence pack' (ticket 2026-DASMG-040). Composes extract_article per URL and aggregates: collect, extract, cross-check, then hand a downstream agent a safe base to write ORIGINAL content from. Modes: curated (`urls` + optional `topic`), hybrid (`urls` + `discover`), discovery (`topic` + `discover.enabled`). Discovery is currently DEFERRED \u2014 when requested it returns a `discovery_unavailable` limitation while curated `urls` still work. Returns `sources` (id,url,canonicalUrl,source,title,publishedAt,authorityTier,extractionMethod,limitations), `verifiedFacts` (claim + corroboratedBy + confidence + authorityTier + asOf), `conflicts` (variants + suggestedResolution), `uniqueClaims` (single-source), `openQuestions`, `verbatimWarnings`, and pack-level `limitations`. Structured EVIDENCE ONLY \u2014 never ready-to-publish prose or brand voice; every claim traces back to a source id. PUBLIC URLs only (inherits the SSRF guard). Persistence/reuse is out of scope (2026-DASMG-041).",
|
|
10618
|
+
inputSchema: {
|
|
10619
|
+
type: "object",
|
|
10620
|
+
properties: {
|
|
10621
|
+
topic: {
|
|
10622
|
+
type: "string",
|
|
10623
|
+
description: "The topic the evidence pack is about (used for context/labelling)."
|
|
10624
|
+
},
|
|
10625
|
+
urls: {
|
|
10626
|
+
type: "array",
|
|
10627
|
+
items: { type: "string" },
|
|
10628
|
+
description: "Curated list of public http(s) URLs to extract and cross-check (max 20)."
|
|
10629
|
+
},
|
|
10630
|
+
discover: {
|
|
10631
|
+
type: "object",
|
|
10632
|
+
description: "Discovery options (currently deferred). enabled=true emits a discovery_unavailable limitation; curated urls still work.",
|
|
10633
|
+
properties: {
|
|
10634
|
+
enabled: { type: "boolean" },
|
|
10635
|
+
maxSources: { type: "number" },
|
|
10636
|
+
recencyDays: { type: "number" },
|
|
10637
|
+
locale: { type: "string" },
|
|
10638
|
+
allowDomains: { type: "array", items: { type: "string" } },
|
|
10639
|
+
blockDomains: { type: "array", items: { type: "string" } }
|
|
10640
|
+
}
|
|
10641
|
+
},
|
|
10642
|
+
includeRawText: {
|
|
10643
|
+
type: "boolean",
|
|
10644
|
+
description: "Reserved for compatibility; claim mining always reads body text up to maxRawTextChars."
|
|
10645
|
+
},
|
|
10646
|
+
maxRawTextChars: {
|
|
10647
|
+
type: "number",
|
|
10648
|
+
description: "Per-source body cap used for claim mining (default 4000, max 20000)."
|
|
10649
|
+
}
|
|
10650
|
+
}
|
|
10651
|
+
}
|
|
10652
|
+
},
|
|
10653
|
+
// ----- Content registry / corpus persistence (2026-DASMG-041) -----
|
|
10654
|
+
{
|
|
10655
|
+
name: "save_content_source",
|
|
10656
|
+
description: "Persist (dedupe + upsert) ONE fetched source \u2014 the durable companion to extract_article (2026-DASMG-041). Pass a `url` (re-extracts internally) and/or already-extracted fields (title/summary/facts/fullText). Dedupes on canonical URL then text hash, and snapshots the previous version into content_snapshots when the text changed. External sources keep their `source_type` so they stay labelled as evidence, never own content. Returns the stored id + a compact summary + whether it was created/updated/snapshotted.",
|
|
10657
|
+
inputSchema: {
|
|
10658
|
+
type: "object",
|
|
10659
|
+
properties: {
|
|
10660
|
+
url: { type: "string", description: "Source URL (required)." },
|
|
10661
|
+
canonicalUrl: { type: "string", description: "Canonical URL used for dedupe." },
|
|
10662
|
+
sourceType: {
|
|
10663
|
+
type: "string",
|
|
10664
|
+
enum: [
|
|
10665
|
+
"own_site",
|
|
10666
|
+
"client_site",
|
|
10667
|
+
"external_source",
|
|
10668
|
+
"competitor",
|
|
10669
|
+
"news",
|
|
10670
|
+
"government",
|
|
10671
|
+
"supplier",
|
|
10672
|
+
"unknown"
|
|
10673
|
+
],
|
|
10674
|
+
description: "Provenance label. own_site/client_site = managed content; the rest are evidence only."
|
|
10675
|
+
},
|
|
10676
|
+
title: { type: "string", description: "Title." },
|
|
10677
|
+
author: { type: "string", description: "Author." },
|
|
10678
|
+
publishedAt: { type: "string", description: "Publish date (any parseable format)." },
|
|
10679
|
+
summary: { type: "string", description: "Short factual summary." },
|
|
10680
|
+
facts: { type: "array", items: { type: "string" }, description: "Key facts." },
|
|
10681
|
+
limitations: { type: "array", items: { type: "string" } },
|
|
10682
|
+
fullText: { type: "string", description: "Extracted body text." },
|
|
10683
|
+
language: { type: "string", description: "Language code." },
|
|
10684
|
+
projectKey: { type: "string", description: "Project/site identifier." },
|
|
10685
|
+
siteKey: { type: "string", description: "Site identifier." },
|
|
10686
|
+
ticketNumber: { type: "string", description: "Related ticket number." },
|
|
10687
|
+
metadata: { type: "object" },
|
|
10688
|
+
reExtract: {
|
|
10689
|
+
type: "boolean",
|
|
10690
|
+
description: "Force a re-fetch even when fields are supplied."
|
|
10691
|
+
}
|
|
10692
|
+
},
|
|
10693
|
+
required: ["url"]
|
|
10694
|
+
}
|
|
10695
|
+
},
|
|
10696
|
+
{
|
|
10697
|
+
name: "get_content_source_by_url",
|
|
10698
|
+
description: "Look up a stored source by URL or canonical URL (2026-DASMG-041). Compact by default (id + summary + counts); set `includeFullText` for the body. Use to answer 'was this link already scanned?' before re-extracting.",
|
|
10699
|
+
inputSchema: {
|
|
10700
|
+
type: "object",
|
|
10701
|
+
properties: {
|
|
10702
|
+
url: { type: "string", description: "URL or canonical URL." },
|
|
10703
|
+
includeFullText: { type: "boolean", description: "Return full_text." }
|
|
10704
|
+
},
|
|
10705
|
+
required: ["url"]
|
|
10706
|
+
}
|
|
10707
|
+
},
|
|
10708
|
+
{
|
|
10709
|
+
name: "get_content_source_by_id",
|
|
10710
|
+
description: "Fetch a stored source by id (2026-DASMG-041). Compact by default; set `includeFullText` for the body.",
|
|
10711
|
+
inputSchema: {
|
|
10712
|
+
type: "object",
|
|
10713
|
+
properties: {
|
|
10714
|
+
id: { type: "string", description: "content_sources.id (uuid)." },
|
|
10715
|
+
includeFullText: { type: "boolean", description: "Return full_text." }
|
|
10716
|
+
},
|
|
10717
|
+
required: ["id"]
|
|
10718
|
+
}
|
|
10719
|
+
},
|
|
10720
|
+
{
|
|
10721
|
+
name: "search_content_sources",
|
|
10722
|
+
description: "Full-text + filter search over stored sources (2026-DASMG-041): query (title/summary/body), domain, sourceType, projectKey, ticketNumber, dateFrom/dateTo. Compact, paginated results. Use to reuse evidence already in the registry.",
|
|
10723
|
+
inputSchema: {
|
|
10724
|
+
type: "object",
|
|
10725
|
+
properties: {
|
|
10726
|
+
query: { type: "string", description: "Free-text search (title/summary/body)." },
|
|
10727
|
+
domain: { type: "string", description: "Exact domain filter." },
|
|
10728
|
+
sourceType: {
|
|
10729
|
+
type: "string",
|
|
10730
|
+
enum: [
|
|
10731
|
+
"own_site",
|
|
10732
|
+
"client_site",
|
|
10733
|
+
"external_source",
|
|
10734
|
+
"competitor",
|
|
10735
|
+
"news",
|
|
10736
|
+
"government",
|
|
10737
|
+
"supplier",
|
|
10738
|
+
"unknown"
|
|
10739
|
+
]
|
|
10740
|
+
},
|
|
10741
|
+
projectKey: { type: "string", description: "Project filter." },
|
|
10742
|
+
ticketNumber: { type: "string", description: "Ticket filter." },
|
|
10743
|
+
dateFrom: { type: "string", description: "Last-seen lower bound (ISO/date)." },
|
|
10744
|
+
dateTo: { type: "string", description: "Last-seen upper bound (ISO/date)." },
|
|
10745
|
+
limit: { type: "integer", description: "Max rows (default 20, max 100)." },
|
|
10746
|
+
offset: { type: "integer", description: "Pagination offset." },
|
|
10747
|
+
includeFullText: { type: "boolean", description: "Return full_text per row." }
|
|
10748
|
+
}
|
|
10749
|
+
}
|
|
10750
|
+
},
|
|
10751
|
+
{
|
|
10752
|
+
name: "save_research_pack",
|
|
10753
|
+
description: "Persist a research_topic evidence pack (2026-DASMG-041): cross-checked verified facts, conflicts, unique claims, open questions. Pass an already-built `pack`, or `urls`/`topic` to research then save. Each source is deduped into content_sources and linked via the join table, so packs can be reused without re-crawling. Returns the pack id + compact counts + linked source ids.",
|
|
10754
|
+
inputSchema: {
|
|
10755
|
+
type: "object",
|
|
10756
|
+
properties: {
|
|
10757
|
+
pack: { type: "object", description: "A research_topic result payload." },
|
|
10758
|
+
urls: {
|
|
10759
|
+
type: "array",
|
|
10760
|
+
items: { type: "string" },
|
|
10761
|
+
description: "URLs to research if no pack is given."
|
|
10762
|
+
},
|
|
10763
|
+
topic: { type: "string", description: "Topic to research if no pack is given." },
|
|
10764
|
+
locale: { type: "string", description: "Locale of the pack." },
|
|
10765
|
+
projectKey: { type: "string", description: "Project identifier." },
|
|
10766
|
+
siteKey: { type: "string", description: "Site identifier." },
|
|
10767
|
+
ticketNumber: { type: "string", description: "Related ticket number." },
|
|
10768
|
+
sourceType: {
|
|
10769
|
+
type: "string",
|
|
10770
|
+
enum: [
|
|
10771
|
+
"own_site",
|
|
10772
|
+
"client_site",
|
|
10773
|
+
"external_source",
|
|
10774
|
+
"competitor",
|
|
10775
|
+
"news",
|
|
10776
|
+
"government",
|
|
10777
|
+
"supplier",
|
|
10778
|
+
"unknown"
|
|
10779
|
+
]
|
|
10780
|
+
},
|
|
10781
|
+
metadata: { type: "object" }
|
|
10782
|
+
}
|
|
10783
|
+
}
|
|
10784
|
+
},
|
|
10785
|
+
{
|
|
10786
|
+
name: "get_research_pack",
|
|
10787
|
+
description: "Fetch a stored evidence pack by id (2026-DASMG-041). Compact by default (counts + provenance); set `includeSources` / `includeClaims` for the full arrays.",
|
|
10788
|
+
inputSchema: {
|
|
10789
|
+
type: "object",
|
|
10790
|
+
properties: {
|
|
10791
|
+
id: { type: "string", description: "content_evidence_packs.id (uuid)." },
|
|
10792
|
+
includeSources: { type: "boolean", description: "Return the source list." },
|
|
10793
|
+
includeClaims: {
|
|
10794
|
+
type: "boolean",
|
|
10795
|
+
description: "Return verified facts / conflicts / unique claims."
|
|
10796
|
+
}
|
|
10797
|
+
},
|
|
10798
|
+
required: ["id"]
|
|
10799
|
+
}
|
|
10800
|
+
},
|
|
10801
|
+
{
|
|
10802
|
+
name: "save_content_corpus_item",
|
|
10803
|
+
description: "Upsert ONE own/managed page into the corpus (2026-DASMG-041), keyed by (projectKey, canonical url) \u2014 the current state of content you own. Pass a `url` (re-extracts) and/or title/h1/headings/fullText/internalLinks. Snapshots the previous version when the text changed. Use this for your/your client's pages, not external evidence.",
|
|
10804
|
+
inputSchema: {
|
|
10805
|
+
type: "object",
|
|
10806
|
+
properties: {
|
|
10807
|
+
projectKey: { type: "string", description: "Project identifier (required)." },
|
|
10808
|
+
url: { type: "string", description: "Page URL (required)." },
|
|
10809
|
+
siteKey: { type: "string", description: "Site identifier." },
|
|
10810
|
+
canonicalUrl: { type: "string", description: "Canonical URL." },
|
|
10811
|
+
sourceType: {
|
|
10812
|
+
type: "string",
|
|
10813
|
+
enum: [
|
|
10814
|
+
"own_site",
|
|
10815
|
+
"client_site",
|
|
10816
|
+
"external_source",
|
|
10817
|
+
"competitor",
|
|
10818
|
+
"news",
|
|
10819
|
+
"government",
|
|
10820
|
+
"supplier",
|
|
10821
|
+
"unknown"
|
|
10822
|
+
],
|
|
10823
|
+
description: "Defaults to own_site."
|
|
10824
|
+
},
|
|
10825
|
+
pagePath: { type: "string", description: "Path within the site." },
|
|
10826
|
+
title: { type: "string", description: "Title." },
|
|
10827
|
+
h1: { type: "string", description: "Main heading." },
|
|
10828
|
+
headings: {
|
|
10829
|
+
type: "array",
|
|
10830
|
+
items: { type: "object" },
|
|
10831
|
+
description: "Headings ({level,text})."
|
|
10832
|
+
},
|
|
10833
|
+
wordCount: { type: "integer", description: "Word count." },
|
|
10834
|
+
fullText: { type: "string", description: "Full page text." },
|
|
10835
|
+
internalLinks: {
|
|
10836
|
+
type: "array",
|
|
10837
|
+
items: { type: "object" },
|
|
10838
|
+
description: "Internal links ({url,text})."
|
|
10839
|
+
},
|
|
10840
|
+
externalLinks: { type: "array", items: { type: "object" } },
|
|
10841
|
+
language: { type: "string", description: "Language code." },
|
|
10842
|
+
ticketNumber: { type: "string", description: "Related ticket number." },
|
|
10843
|
+
metadata: { type: "object" },
|
|
10844
|
+
reExtract: {
|
|
10845
|
+
type: "boolean",
|
|
10846
|
+
description: "Force a re-fetch even when fields are supplied."
|
|
10847
|
+
}
|
|
10848
|
+
},
|
|
10849
|
+
required: ["projectKey", "url"]
|
|
10850
|
+
}
|
|
10851
|
+
},
|
|
10852
|
+
{
|
|
10853
|
+
name: "list_project_content_corpus",
|
|
10854
|
+
description: "List corpus items for a project (2026-DASMG-041), filter by domain/sourceType/query. Compact, paginated. Use to see content coverage and reuse own pages.",
|
|
10855
|
+
inputSchema: {
|
|
10856
|
+
type: "object",
|
|
10857
|
+
properties: {
|
|
10858
|
+
projectKey: { type: "string", description: "Project identifier (required)." },
|
|
10859
|
+
domain: { type: "string", description: "Domain filter." },
|
|
10860
|
+
sourceType: {
|
|
10861
|
+
type: "string",
|
|
10862
|
+
enum: [
|
|
10863
|
+
"own_site",
|
|
10864
|
+
"client_site",
|
|
10865
|
+
"external_source",
|
|
10866
|
+
"competitor",
|
|
10867
|
+
"news",
|
|
10868
|
+
"government",
|
|
10869
|
+
"supplier",
|
|
10870
|
+
"unknown"
|
|
10871
|
+
]
|
|
10872
|
+
},
|
|
10873
|
+
query: { type: "string", description: "Free-text search." },
|
|
10874
|
+
limit: { type: "integer", description: "Max rows (default 20)." },
|
|
10875
|
+
offset: { type: "integer", description: "Pagination offset." },
|
|
10876
|
+
includeFullText: { type: "boolean", description: "Return full_text per row." }
|
|
10877
|
+
},
|
|
10878
|
+
required: ["projectKey"]
|
|
10879
|
+
}
|
|
10880
|
+
},
|
|
10881
|
+
{
|
|
10882
|
+
name: "get_content_context_for_project",
|
|
10883
|
+
description: "Aggregate context for a content agent (2026-DASMG-041): recent corpus items, recent evidence packs and related sources for a project (optionally focused by topic or url), plus totals. One call to brief an agent on what already exists before it writes.",
|
|
10884
|
+
inputSchema: {
|
|
10885
|
+
type: "object",
|
|
10886
|
+
properties: {
|
|
10887
|
+
projectKey: { type: "string", description: "Project identifier (required)." },
|
|
10888
|
+
topic: { type: "string", description: "Optional topic focus." },
|
|
10889
|
+
url: { type: "string", description: "Optional url focus." },
|
|
10890
|
+
limit: { type: "integer", description: "Max items per section (default 10)." }
|
|
10891
|
+
},
|
|
10892
|
+
required: ["projectKey"]
|
|
10893
|
+
}
|
|
10894
|
+
},
|
|
10895
|
+
{
|
|
10896
|
+
name: "save_content_quality_run",
|
|
10897
|
+
description: "Store a reference to an MG SEO CLI / audit run (2026-DASMG-041): score, flags and report, attached to a corpus item, source or url. MG Dashboard stores the run; the CLI does the scoring (the CLI stays the QC layer).",
|
|
10898
|
+
inputSchema: {
|
|
10899
|
+
type: "object",
|
|
10900
|
+
properties: {
|
|
10901
|
+
tool: { type: "string", description: "Tool name, e.g. mg-seo (required)." },
|
|
10902
|
+
corpusItemId: { type: "string", description: "content_corpus_items.id." },
|
|
10903
|
+
contentSourceId: { type: "string", description: "content_sources.id." },
|
|
10904
|
+
projectKey: { type: "string", description: "Project identifier." },
|
|
10905
|
+
siteKey: { type: "string", description: "Site identifier." },
|
|
10906
|
+
url: { type: "string", description: "Audited URL." },
|
|
10907
|
+
canonicalUrl: { type: "string", description: "Canonical URL." },
|
|
10908
|
+
toolVersion: { type: "string", description: "Tool version." },
|
|
10909
|
+
score: { type: "integer", description: "Overall score 0-100." },
|
|
10910
|
+
flags: {
|
|
10911
|
+
type: "array",
|
|
10912
|
+
items: { type: "object" },
|
|
10913
|
+
description: "Flags ({code,severity,message})."
|
|
10914
|
+
},
|
|
10915
|
+
report: { type: "object", description: "Full report payload." },
|
|
10916
|
+
reportUrl: { type: "string", description: "Link to the full report." },
|
|
10917
|
+
ticketNumber: { type: "string", description: "Related ticket number." },
|
|
10918
|
+
runAt: { type: "string", description: "Run timestamp (ISO)." }
|
|
10919
|
+
},
|
|
10920
|
+
required: ["tool"]
|
|
10921
|
+
}
|
|
10922
|
+
},
|
|
10923
|
+
{
|
|
10924
|
+
name: "list_content_snapshots",
|
|
10925
|
+
description: "List historical snapshots for a corpus item or source (2026-DASMG-041) by id, url or canonical url, newest first. Compact; set `includeFullText` for the captured body. Use to see how content changed over time.",
|
|
10926
|
+
inputSchema: {
|
|
10927
|
+
type: "object",
|
|
10928
|
+
properties: {
|
|
10929
|
+
corpusItemId: { type: "string", description: "content_corpus_items.id." },
|
|
10930
|
+
contentSourceId: { type: "string", description: "content_sources.id." },
|
|
10931
|
+
url: { type: "string", description: "URL to match." },
|
|
10932
|
+
canonicalUrl: { type: "string", description: "Canonical URL to match." },
|
|
10933
|
+
limit: { type: "integer", description: "Max rows (default 20)." },
|
|
10934
|
+
includeFullText: { type: "boolean", description: "Return captured full_text." }
|
|
10935
|
+
}
|
|
10936
|
+
}
|
|
10937
|
+
},
|
|
10938
|
+
{
|
|
10939
|
+
name: "link_content_pack_to_ticket",
|
|
10940
|
+
description: "Attach a stored evidence pack to a ticket number (2026-DASMG-041).",
|
|
10941
|
+
inputSchema: {
|
|
10942
|
+
type: "object",
|
|
10943
|
+
properties: {
|
|
10944
|
+
packId: { type: "string", description: "content_evidence_packs.id (uuid)." },
|
|
10945
|
+
ticketNumber: { type: "string", description: "Ticket number." }
|
|
10946
|
+
},
|
|
10947
|
+
required: ["packId", "ticketNumber"]
|
|
10948
|
+
}
|
|
10949
|
+
},
|
|
10950
|
+
{
|
|
10951
|
+
name: "prune_content_snapshots",
|
|
10952
|
+
description: "Retention cleanup (2026-DASMG-041): keep the newest N snapshots per item and drop the rest older than the retention window. Defaults: keepPerItem 20, olderThanDays 365. Returns how many were pruned.",
|
|
10953
|
+
inputSchema: {
|
|
10954
|
+
type: "object",
|
|
10955
|
+
properties: {
|
|
10956
|
+
keepPerItem: { type: "integer", description: "Snapshots to keep per item (default 20)." },
|
|
10957
|
+
olderThanDays: {
|
|
10958
|
+
type: "integer",
|
|
10959
|
+
description: "Only prune snapshots older than this many days (default 365)."
|
|
10960
|
+
}
|
|
10961
|
+
}
|
|
10962
|
+
}
|
|
10963
|
+
},
|
|
10359
10964
|
{
|
|
10360
10965
|
name: "search-team-memory",
|
|
10361
10966
|
description: "Search the team's ENTIRE past Cursor history (every developer, every project) for how something was handled before. Use this FIRST, before investigating from scratch, whenever you: hit a non-trivial bug or error, are about to build something that may have been done before, need a project-specific convention/gotcha, or the user asks 'have we done X', 'how did we fix Y', or 'did we solve this already'. Hybrid semantic + keyword search over mirrored conversations. Returns ranked past chats with repo, date, a solution snippet and a similarity score. Phrase the query as the problem in natural language (e.g. 'release pipeline PM2 deploy fails with module not found').",
|
|
@@ -10585,6 +11190,121 @@ async function executeToolCall(name, a, _serverId) {
|
|
|
10585
11190
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
10586
11191
|
};
|
|
10587
11192
|
}
|
|
11193
|
+
// ----- Public-web extraction (article tools) -----
|
|
11194
|
+
case "extract_article": {
|
|
11195
|
+
const url = typeof a.url === "string" ? a.url.trim() : "";
|
|
11196
|
+
if (!url) {
|
|
11197
|
+
return { content: [{ type: "text", text: "Error: url is required" }] };
|
|
11198
|
+
}
|
|
11199
|
+
const res = await fetch(`${dashboardBaseUrl}/api/tools/extract-article`, {
|
|
11200
|
+
method: "POST",
|
|
11201
|
+
headers: {
|
|
11202
|
+
"content-type": "application/json",
|
|
11203
|
+
authorization: `Bearer ${apiKey}`
|
|
11204
|
+
},
|
|
11205
|
+
body: JSON.stringify({
|
|
11206
|
+
url,
|
|
11207
|
+
includeRawText: typeof a.includeRawText === "boolean" ? a.includeRawText : void 0,
|
|
11208
|
+
maxRawTextChars: typeof a.maxRawTextChars === "number" ? a.maxRawTextChars : void 0
|
|
11209
|
+
})
|
|
11210
|
+
});
|
|
11211
|
+
if (!res.ok) {
|
|
11212
|
+
const detail = await res.text().catch(() => "");
|
|
11213
|
+
return {
|
|
11214
|
+
content: [
|
|
11215
|
+
{
|
|
11216
|
+
type: "text",
|
|
11217
|
+
text: `Error: extract_article failed (${res.status}). ${detail.slice(0, 300)}`
|
|
11218
|
+
}
|
|
11219
|
+
]
|
|
11220
|
+
};
|
|
11221
|
+
}
|
|
11222
|
+
const data = await res.json();
|
|
11223
|
+
return {
|
|
11224
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
11225
|
+
};
|
|
11226
|
+
}
|
|
11227
|
+
case "research_topic": {
|
|
11228
|
+
const urls = Array.isArray(a.urls) ? a.urls.filter((u) => typeof u === "string") : void 0;
|
|
11229
|
+
const topic = typeof a.topic === "string" ? a.topic.trim() : void 0;
|
|
11230
|
+
if ((!urls || urls.length === 0) && !topic) {
|
|
11231
|
+
return {
|
|
11232
|
+
content: [
|
|
11233
|
+
{ type: "text", text: "Error: provide `urls` (curated) and/or a `topic`" }
|
|
11234
|
+
]
|
|
11235
|
+
};
|
|
11236
|
+
}
|
|
11237
|
+
const res = await fetch(`${dashboardBaseUrl}/api/tools/research-topic`, {
|
|
11238
|
+
method: "POST",
|
|
11239
|
+
headers: {
|
|
11240
|
+
"content-type": "application/json",
|
|
11241
|
+
authorization: `Bearer ${apiKey}`
|
|
11242
|
+
},
|
|
11243
|
+
body: JSON.stringify({
|
|
11244
|
+
topic,
|
|
11245
|
+
urls,
|
|
11246
|
+
discover: a.discover && typeof a.discover === "object" ? a.discover : void 0,
|
|
11247
|
+
includeRawText: typeof a.includeRawText === "boolean" ? a.includeRawText : void 0,
|
|
11248
|
+
maxRawTextChars: typeof a.maxRawTextChars === "number" ? a.maxRawTextChars : void 0
|
|
11249
|
+
})
|
|
11250
|
+
});
|
|
11251
|
+
if (!res.ok) {
|
|
11252
|
+
const detail = await res.text().catch(() => "");
|
|
11253
|
+
return {
|
|
11254
|
+
content: [
|
|
11255
|
+
{
|
|
11256
|
+
type: "text",
|
|
11257
|
+
text: `Error: research_topic failed (${res.status}). ${detail.slice(0, 300)}`
|
|
11258
|
+
}
|
|
11259
|
+
]
|
|
11260
|
+
};
|
|
11261
|
+
}
|
|
11262
|
+
const data = await res.json();
|
|
11263
|
+
return {
|
|
11264
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
11265
|
+
};
|
|
11266
|
+
}
|
|
11267
|
+
// ----- Content registry / corpus persistence (2026-DASMG-041) -----
|
|
11268
|
+
// All content-registry tools proxy to one dispatcher route; the tool name
|
|
11269
|
+
// is the dispatcher `action` and the args are forwarded verbatim as
|
|
11270
|
+
// `params` (the route validates them with the matching Zod schema).
|
|
11271
|
+
case "save_content_source":
|
|
11272
|
+
case "get_content_source_by_url":
|
|
11273
|
+
case "get_content_source_by_id":
|
|
11274
|
+
case "search_content_sources":
|
|
11275
|
+
case "save_research_pack":
|
|
11276
|
+
case "get_research_pack":
|
|
11277
|
+
case "save_content_corpus_item":
|
|
11278
|
+
case "list_project_content_corpus":
|
|
11279
|
+
case "get_content_context_for_project":
|
|
11280
|
+
case "save_content_quality_run":
|
|
11281
|
+
case "list_content_snapshots":
|
|
11282
|
+
case "link_content_pack_to_ticket":
|
|
11283
|
+
case "prune_content_snapshots": {
|
|
11284
|
+
const res = await fetch(`${dashboardBaseUrl}/api/tools/content-registry`, {
|
|
11285
|
+
method: "POST",
|
|
11286
|
+
headers: {
|
|
11287
|
+
"content-type": "application/json",
|
|
11288
|
+
authorization: `Bearer ${apiKey}`
|
|
11289
|
+
},
|
|
11290
|
+
body: JSON.stringify({ action: name, params: a })
|
|
11291
|
+
});
|
|
11292
|
+
if (!res.ok) {
|
|
11293
|
+
const detail = await res.text().catch(() => "");
|
|
11294
|
+
return {
|
|
11295
|
+
content: [
|
|
11296
|
+
{
|
|
11297
|
+
type: "text",
|
|
11298
|
+
text: `Error: ${name} failed (${res.status}). ${detail.slice(0, 300)}`
|
|
11299
|
+
}
|
|
11300
|
+
]
|
|
11301
|
+
};
|
|
11302
|
+
}
|
|
11303
|
+
const data = await res.json();
|
|
11304
|
+
return {
|
|
11305
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
11306
|
+
};
|
|
11307
|
+
}
|
|
10588
11308
|
// ----- Team memory -----
|
|
10589
11309
|
case "search-team-memory": {
|
|
10590
11310
|
const query = typeof a.query === "string" ? a.query.trim() : "";
|
|
@@ -10626,7 +11346,7 @@ async function executeToolCall(name, a, _serverId) {
|
|
|
10626
11346
|
]
|
|
10627
11347
|
};
|
|
10628
11348
|
}
|
|
10629
|
-
const lines = data.hits.map((hit,
|
|
11349
|
+
const lines = data.hits.map((hit, index6) => {
|
|
10630
11350
|
const when = hit.lastMessageAt ? new Date(hit.lastMessageAt).toISOString().slice(0, 10) : "unknown date";
|
|
10631
11351
|
const repo = hit.repo ?? "unknown repo";
|
|
10632
11352
|
const sim = hit.similarity !== null ? ` \xB7 ${(hit.similarity * 100).toFixed(0)}% match` : "";
|
|
@@ -10636,7 +11356,7 @@ async function executeToolCall(name, a, _serverId) {
|
|
|
10636
11356
|
...Array.isArray(hit.tech) ? hit.tech.slice(0, 4) : []
|
|
10637
11357
|
].filter(Boolean);
|
|
10638
11358
|
const tags = facets.length > 0 ? ` \xB7 ${facets.join(", ")}` : "";
|
|
10639
|
-
return `${
|
|
11359
|
+
return `${index6 + 1}. [${repo} \xB7 ${when}${sim}${tags}] ${hit.title}
|
|
10640
11360
|
id: ${hit.id}
|
|
10641
11361
|
${(hit.summary ?? hit.snippet).replace(/\s+/g, " ").slice(0, 500)}`;
|
|
10642
11362
|
});
|
|
@@ -11821,8 +12541,8 @@ ${sample.join("\n")}${files.length > 5 ? `
|
|
|
11821
12541
|
};
|
|
11822
12542
|
const filtered = sortRows(applyFilter(only.rows));
|
|
11823
12543
|
if (format === "json") {
|
|
11824
|
-
const
|
|
11825
|
-
return { content: [{ type: "text", text:
|
|
12544
|
+
const text9 = filtered.map((c) => JSON.stringify(c)).join("\n") || "(no containers)";
|
|
12545
|
+
return { content: [{ type: "text", text: text9 }] };
|
|
11826
12546
|
}
|
|
11827
12547
|
if (groupByProject) {
|
|
11828
12548
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -11849,8 +12569,8 @@ ${sample.join("\n")}${files.length > 5 ? `
|
|
|
11849
12569
|
}
|
|
11850
12570
|
const header = `${"NAMES".padEnd(36)} ${"IMAGE".padEnd(40)} ${"STATUS".padEnd(24)} ${"HEALTH".padEnd(10)} ${"PORTS".padEnd(40)} PROJECT`;
|
|
11851
12571
|
const body = filtered.map((r) => `${fmtRow(r)} ${r.Project || ""}`);
|
|
11852
|
-
const
|
|
11853
|
-
return { content: [{ type: "text", text:
|
|
12572
|
+
const text8 = filtered.length === 0 ? "(no containers match)" : [header, ...body].join("\n");
|
|
12573
|
+
return { content: [{ type: "text", text: text8 }] };
|
|
11854
12574
|
}
|
|
11855
12575
|
if (format === "json") {
|
|
11856
12576
|
const lines = [];
|