@desplega.ai/agent-swarm 1.110.0 → 1.112.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/README.md +1 -1
- package/dist/{actions-837ktebk.js → actions-eckb4g8r.js} +6 -6
- package/dist/{app-85h97pg2.js → app-b05sgm02.js} +3 -3
- package/dist/{assistant-yhrta0c5.js → assistant-7255sbyw.js} +9 -9
- package/dist/{boot-reembed-5sd1t9r4.js → boot-reembed-y1bt3ttn.js} +4 -4
- package/dist/{boot-reembed-8xmfv2pq.js → boot-reembed-yv7awg4y.js} +3 -3
- package/dist/{boot-scrub-logs-fdw8wbtq.js → boot-scrub-logs-ddyaeqar.js} +2 -2
- package/dist/{claude-managed-setup-yhbcx0bz.js → claude-managed-setup-cm05wtek.js} +1 -1
- package/dist/{cli-8ps5wsk9.js → cli-0c3n0eba.js} +1 -1
- package/dist/{cli-kna0r84e.js → cli-15598a4k.js} +599 -51
- package/dist/{cli-8s0swp2t.js → cli-30d2ct3k.js} +3 -3
- package/dist/{cli-2h86bsfe.js → cli-3r5y6ayj.js} +2 -2
- package/dist/{cli-6ttdv68b.js → cli-52tbq17n.js} +1 -1
- package/dist/{cli-k6ssh5dk.js → cli-56s4ehzw.js} +1 -1
- package/dist/{cli-m3enfr8q.js → cli-6wscj9z0.js} +4 -4
- package/dist/{cli-5kav4x1a.js → cli-709t753c.js} +7 -2
- package/dist/{cli-g44ffxk5.js → cli-76d1vtvq.js} +11 -2
- package/dist/{cli-gb7dqnna.js → cli-7xkwgkrs.js} +1 -1
- package/dist/{cli-kg9v8h1w.js → cli-8f0dwca2.js} +1 -1
- package/dist/{cli-zw3cmjh6.js → cli-94vzr3nq.js} +4 -4
- package/dist/{cli-d8vmtyyr.js → cli-951w7c79.js} +1 -1
- package/dist/{cli-dhph6q98.js → cli-9p0qhead.js} +1 -1
- package/dist/{cli-vfyhrk9k.js → cli-a5e8n5hg.js} +1 -1
- package/dist/{cli-w6zp4qcx.js → cli-ceewqajw.js} +37 -39
- package/dist/{cli-r19ttke3.js → cli-h9jx5mag.js} +4 -4
- package/dist/{cli-4trfcacj.js → cli-hm27prrc.js} +5 -5
- package/dist/{cli-gtt1f14h.js → cli-pt7c7qxz.js} +3 -3
- package/dist/{cli-sjm0e1pa.js → cli-r1btc895.js} +2 -2
- package/dist/{cli-3baz70yp.js → cli-rp8pca98.js} +1 -1
- package/dist/{cli-dy01kwxk.js → cli-s9pj3c7z.js} +2 -2
- package/dist/{cli-0pzg1f8z.js → cli-tch0ypp6.js} +71 -24
- package/dist/{cli-3t97r3y1.js → cli-tg0f2wmz.js} +5 -5
- package/dist/{cli-amncg1dq.js → cli-tk9vyqzh.js} +66 -9
- package/dist/{cli-cwety9qq.js → cli-tp7e60s3.js} +4 -2
- package/dist/cli.js +11 -11
- package/dist/{codex-adapter-ebs12jef.js → codex-adapter-5cmy858w.js} +3 -3
- package/dist/{codex-login-4sywkz6v.js → codex-login-0h18cy3e.js} +1 -1
- package/dist/{codex-session-runner-3bx0b1hn.js → codex-session-runner-46q8mrba.js} +3 -3
- package/dist/{commands-3fh75nzv.js → commands-14g7taf4.js} +2 -2
- package/dist/{db-kwfxkhpw.js → db-3tge4jrn.js} +2 -2
- package/dist/{handlers-jj8rzetb.js → handlers-sy64egg6.js} +9 -9
- package/dist/{hook-1b45zzz1.js → hook-rnkzw8vp.js} +3 -3
- package/dist/{http-71964f7a.js → http-xx9xptcs.js} +234 -37
- package/dist/{index-e01wmtj0.js → index-3wr6v2mm.js} +7 -7
- package/dist/{index-464v93y6.js → index-brma88d4.js} +9 -9
- package/dist/{index-c35g752k.js → index-ha8e9xn2.js} +7 -11
- package/dist/{index-zm0x6vmf.js → index-zfx5p959.js} +8 -8
- package/dist/{keepalive-2gxqjgrx.js → keepalive-rbz05zad.js} +4 -4
- package/dist/{lead-twm4zp8y.js → lead-s790v7r0.js} +20 -20
- package/dist/{maintenance-1htsnyz6.js → maintenance-420rmadb.js} +4 -4
- package/dist/{onboard-1y1pdzt5.js → onboard-jgqamqnw.js} +2 -2
- package/dist/{otel-impl-pw9tnc80.js → otel-impl-ch3n3783.js} +1 -1
- package/dist/{pi-mono-adapter-ayr3akk8.js → pi-mono-adapter-kxjxpfgy.js} +2 -2
- package/dist/{pricing-refresh-tqvvrk7a.js → pricing-refresh-rmtrby6h.js} +4 -4
- package/dist/{seed-pricing-f23sehzq.js → seed-pricing-1szjzfj1.js} +3 -3
- package/dist/{setup-jtw9rz9t.js → setup-8jfzkyzv.js} +2 -2
- package/dist/{worker-jxrmzwb9.js → worker-40vq0pm8.js} +20 -20
- package/openapi.json +1010 -2
- package/package.json +1 -1
- package/src/be/memory/constants.ts +7 -0
- package/src/be/memory/graph-expansion.ts +167 -0
- package/src/be/memory/link-resolver.ts +81 -17
- package/src/be/memory/links-store.ts +208 -0
- package/src/be/memory/providers/sqlite-store.ts +2 -2
- package/src/be/memory/raters/retrieval.ts +2 -1
- package/src/be/memory/types.ts +1 -1
- package/src/be/memory/usefulness-stats.ts +259 -0
- package/src/be/migrations/107_memory_usefulness_indexes.sql +12 -0
- package/src/be/scripts/typecheck.ts +2 -0
- package/src/be/seed-scripts/catalog/smart-recall.ts +1 -5
- package/src/commands/runner.ts +154 -8
- package/src/github/handlers.ts +128 -25
- package/src/github/mentions-aliases.test.ts +4 -3
- package/src/http/codex-oauth-keep-warm.ts +107 -0
- package/src/http/index.ts +2 -0
- package/src/http/memory.ts +73 -6
- package/src/providers/codex-adapter.ts +66 -2
- package/src/providers/codex-oauth/auth-json.ts +18 -9
- package/src/providers/codex-oauth/flow.ts +1 -1
- package/src/providers/codex-oauth/storage.ts +88 -6
- package/src/providers/codex-oauth/types.ts +1 -1
- package/src/scripts-runtime/sdk-allowlist.ts +2 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +2 -0
- package/src/server.ts +4 -0
- package/src/slack/message-text.ts +25 -0
- package/src/tests/codex-adapter-pool-auth-revalidate.test.ts +113 -11
- package/src/tests/codex-adapter.test.ts +27 -0
- package/src/tests/codex-oauth-keep-warm.test.ts +268 -0
- package/src/tests/codex-oauth-refresh-lock.test.ts +4 -4
- package/src/tests/codex-oauth-storage.test.ts +149 -6
- package/src/tests/codex-oauth.test.ts +12 -0
- package/src/tests/create-page-tool.test.ts +7 -0
- package/src/tests/entrypoint-codex-oauth-seed.test.ts +124 -0
- package/src/tests/github-event-filter.test.ts +3 -2
- package/src/tests/github-handlers-inline-comments.test.ts +114 -24
- package/src/tests/jira-webhook-lifecycle.test.ts +7 -0
- package/src/tests/memory-edit.test.ts +95 -0
- package/src/tests/memory-get-tool.test.ts +138 -0
- package/src/tests/memory-graph-expansion.test.ts +421 -0
- package/src/tests/memory-http-recall-gating.test.ts +13 -0
- package/src/tests/memory-link-resolver.test.ts +228 -2
- package/src/tests/memory-usefulness-endpoint.test.ts +392 -0
- package/src/tests/runner-repo-autostash.test.ts +168 -1
- package/src/tests/script-workflows-runtime-e2e.test.ts +1 -0
- package/src/tests/slack-delete.test.ts +157 -0
- package/src/tests/slack-message-text.test.ts +37 -1
- package/src/tests/slack-update.test.ts +167 -0
- package/src/tests/slack-upload-file.test.ts +135 -0
- package/src/tests/tool-annotations.test.ts +3 -0
- package/src/tools/inject-learning.ts +1 -0
- package/src/tools/memory-edit.ts +3 -2
- package/src/tools/memory-get.ts +66 -1
- package/src/tools/memory-search.ts +13 -3
- package/src/tools/slack-delete.ts +102 -0
- package/src/tools/slack-reply.ts +11 -3
- package/src/tools/slack-start-thread.ts +2 -0
- package/src/tools/slack-update.ts +127 -0
- package/src/tools/slack-upload-file.ts +11 -1
- package/src/tools/tool-config.ts +3 -1
|
@@ -14,13 +14,13 @@ import {
|
|
|
14
14
|
getOAuthApp,
|
|
15
15
|
getOAuthTokens,
|
|
16
16
|
upsertOAuthApp
|
|
17
|
-
} from "./cli-
|
|
17
|
+
} from "./cli-52tbq17n.js";
|
|
18
18
|
import {
|
|
19
19
|
startPricingRefreshLoop
|
|
20
|
-
} from "./cli-
|
|
20
|
+
} from "./cli-r1btc895.js";
|
|
21
21
|
import {
|
|
22
22
|
seedPricingFromModelsDev
|
|
23
|
-
} from "./cli-
|
|
23
|
+
} from "./cli-rp8pca98.js";
|
|
24
24
|
import {
|
|
25
25
|
createTrackerAgentMapping,
|
|
26
26
|
createTrackerSync,
|
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
repointTrackerSyncBySwarmId,
|
|
36
36
|
updateTrackerSync,
|
|
37
37
|
updateTrackerSyncSwarmId
|
|
38
|
-
} from "./cli-
|
|
38
|
+
} from "./cli-56s4ehzw.js";
|
|
39
39
|
import {
|
|
40
40
|
ZodFirstPartyTypeKind,
|
|
41
41
|
objectType
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
ensure,
|
|
46
46
|
require_dist,
|
|
47
47
|
runScheduleNow
|
|
48
|
-
} from "./cli-
|
|
48
|
+
} from "./cli-94vzr3nq.js";
|
|
49
49
|
import {
|
|
50
50
|
TriggerSchemaError,
|
|
51
51
|
applyDefinitionPatch,
|
|
@@ -57,7 +57,7 @@ import {
|
|
|
57
57
|
startWorkflowExecution,
|
|
58
58
|
validateDefinition,
|
|
59
59
|
validateJsonSchema
|
|
60
|
-
} from "./cli-
|
|
60
|
+
} from "./cli-tg0f2wmz.js";
|
|
61
61
|
import {
|
|
62
62
|
getApiKey
|
|
63
63
|
} from "./cli-f14fvzag.js";
|
|
@@ -73,24 +73,29 @@ import {
|
|
|
73
73
|
setScriptConnectionEnabled,
|
|
74
74
|
upsertCredentialBinding,
|
|
75
75
|
upsertScriptConnection
|
|
76
|
-
} from "./cli-
|
|
76
|
+
} from "./cli-3r5y6ayj.js";
|
|
77
77
|
import {
|
|
78
78
|
CANDIDATE_SET_MULTIPLIER,
|
|
79
|
+
computeScore,
|
|
79
80
|
getEmbeddingProvider,
|
|
80
81
|
getMemoryStore,
|
|
81
82
|
init_constants,
|
|
82
83
|
init_reranker,
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
init_sqlite_store,
|
|
85
|
+
isGraphExpansionEnabled,
|
|
86
|
+
rerank,
|
|
87
|
+
rowToCandidate
|
|
88
|
+
} from "./cli-709t753c.js";
|
|
85
89
|
import {
|
|
86
90
|
createAdditiveBuffer,
|
|
87
|
-
extractSlackMessageText
|
|
88
|
-
|
|
91
|
+
extractSlackMessageText,
|
|
92
|
+
parseSlackTs
|
|
93
|
+
} from "./cli-76d1vtvq.js";
|
|
89
94
|
import {
|
|
90
95
|
getSlackApp,
|
|
91
96
|
startSlackApp,
|
|
92
97
|
stopSlackApp
|
|
93
|
-
} from "./cli-
|
|
98
|
+
} from "./cli-hm27prrc.js";
|
|
94
99
|
import {
|
|
95
100
|
findOrCreateUserByEmail,
|
|
96
101
|
findUserByEmail,
|
|
@@ -102,7 +107,7 @@ import {
|
|
|
102
107
|
recordIdentityEvent,
|
|
103
108
|
resolveUserByToken,
|
|
104
109
|
unlinkIdentity
|
|
105
|
-
} from "./cli-
|
|
110
|
+
} from "./cli-7xkwgkrs.js";
|
|
106
111
|
import {
|
|
107
112
|
markdownToSlack
|
|
108
113
|
} from "./cli-6xd0bvf8.js";
|
|
@@ -113,7 +118,7 @@ import {
|
|
|
113
118
|
createTaskWithSiblingAwareness,
|
|
114
119
|
githubContextKey,
|
|
115
120
|
linearContextKey
|
|
116
|
-
} from "./cli-
|
|
121
|
+
} from "./cli-0c3n0eba.js";
|
|
117
122
|
import {
|
|
118
123
|
getAppUrl,
|
|
119
124
|
getMcpBaseUrl,
|
|
@@ -342,7 +347,7 @@ import {
|
|
|
342
347
|
upsertService,
|
|
343
348
|
upsertSwarmConfig,
|
|
344
349
|
validateConfigValue
|
|
345
|
-
} from "./cli-
|
|
350
|
+
} from "./cli-951w7c79.js";
|
|
346
351
|
import {
|
|
347
352
|
init_zod
|
|
348
353
|
} from "./cli-q21d49ac.js";
|
|
@@ -386,7 +391,7 @@ import {
|
|
|
386
391
|
import {
|
|
387
392
|
init_package,
|
|
388
393
|
package_default
|
|
389
|
-
} from "./cli-
|
|
394
|
+
} from "./cli-8f0dwca2.js";
|
|
390
395
|
import {
|
|
391
396
|
init_secret_scrubber,
|
|
392
397
|
refreshSecretScrubberCache,
|
|
@@ -19668,7 +19673,7 @@ async function initOtel(serviceRole = process.env.AGENT_ROLE || "api") {
|
|
|
19668
19673
|
return;
|
|
19669
19674
|
initialized2 = true;
|
|
19670
19675
|
try {
|
|
19671
|
-
const impl = await import("./otel-impl-
|
|
19676
|
+
const impl = await import("./otel-impl-ch3n3783.js");
|
|
19672
19677
|
await impl.boot(serviceRole);
|
|
19673
19678
|
realWithSpan = impl.withSpan;
|
|
19674
19679
|
realStartSpan = impl.startSpan;
|
|
@@ -21616,7 +21621,8 @@ ${learning}`;
|
|
|
21616
21621
|
scope: "swarm",
|
|
21617
21622
|
name: `Lead feedback: ${category} — ${learning.slice(0, 60)}`,
|
|
21618
21623
|
content,
|
|
21619
|
-
source: "manual"
|
|
21624
|
+
source: "manual",
|
|
21625
|
+
tags: [category]
|
|
21620
21626
|
});
|
|
21621
21627
|
try {
|
|
21622
21628
|
const provider = getEmbeddingProvider();
|
|
@@ -23341,6 +23347,24 @@ function resolveWikilinksToMemoryIds(agentId, links) {
|
|
|
23341
23347
|
return link;
|
|
23342
23348
|
});
|
|
23343
23349
|
}
|
|
23350
|
+
var INSERT_LINK_SQL = `INSERT OR IGNORE INTO memory_link
|
|
23351
|
+
(id, from_memory_id, linkType, targetKind, targetId, strength, resolver, sourceText, metadata, createdAt, updatedAt)
|
|
23352
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
|
|
23353
|
+
function insertLinkArgs(memoryId, link, now) {
|
|
23354
|
+
return [
|
|
23355
|
+
crypto.randomUUID(),
|
|
23356
|
+
memoryId,
|
|
23357
|
+
link.linkType,
|
|
23358
|
+
link.targetKind,
|
|
23359
|
+
link.targetId,
|
|
23360
|
+
link.strength,
|
|
23361
|
+
link.resolver,
|
|
23362
|
+
link.sourceText,
|
|
23363
|
+
link.metadata ? JSON.stringify(link.metadata) : null,
|
|
23364
|
+
now,
|
|
23365
|
+
now
|
|
23366
|
+
];
|
|
23367
|
+
}
|
|
23344
23368
|
function storeLinks(memoryId, agentId, content) {
|
|
23345
23369
|
const links = resolveLinks(content);
|
|
23346
23370
|
if (links.length === 0)
|
|
@@ -23348,12 +23372,34 @@ function storeLinks(memoryId, agentId, content) {
|
|
|
23348
23372
|
const resolved = resolveWikilinksToMemoryIds(agentId, links);
|
|
23349
23373
|
const db = getDb();
|
|
23350
23374
|
const now = new Date().toISOString();
|
|
23351
|
-
const insert = db.prepare(
|
|
23352
|
-
|
|
23353
|
-
|
|
23375
|
+
const insert = db.prepare(INSERT_LINK_SQL);
|
|
23376
|
+
db.transaction(() => {
|
|
23377
|
+
for (const link of resolved) {
|
|
23378
|
+
insert.run(...insertLinkArgs(memoryId, link, now));
|
|
23379
|
+
}
|
|
23380
|
+
})();
|
|
23381
|
+
}
|
|
23382
|
+
function linkIdentity2(link) {
|
|
23383
|
+
return JSON.stringify([link.linkType, link.targetKind, link.targetId, link.sourceText ?? null]);
|
|
23384
|
+
}
|
|
23385
|
+
function refreshLinks(memoryId, agentId, content) {
|
|
23386
|
+
const resolved = resolveWikilinksToMemoryIds(agentId, resolveLinks(content));
|
|
23387
|
+
const nextIdentities = new Set(resolved.map((link) => linkIdentity2(link)));
|
|
23388
|
+
const db = getDb();
|
|
23389
|
+
const now = new Date().toISOString();
|
|
23390
|
+
const selectExisting = db.prepare(`SELECT id, linkType, targetKind, targetId, sourceText
|
|
23391
|
+
FROM memory_link
|
|
23392
|
+
WHERE from_memory_id = ? AND linkType != 'sequel'`);
|
|
23393
|
+
const deleteById = db.prepare("DELETE FROM memory_link WHERE id = ?");
|
|
23394
|
+
const insert = db.prepare(INSERT_LINK_SQL);
|
|
23354
23395
|
db.transaction(() => {
|
|
23396
|
+
for (const row of selectExisting.all(memoryId)) {
|
|
23397
|
+
if (!nextIdentities.has(linkIdentity2(row))) {
|
|
23398
|
+
deleteById.run(row.id);
|
|
23399
|
+
}
|
|
23400
|
+
}
|
|
23355
23401
|
for (const link of resolved) {
|
|
23356
|
-
insert.run(
|
|
23402
|
+
insert.run(...insertLinkArgs(memoryId, link, now));
|
|
23357
23403
|
}
|
|
23358
23404
|
})();
|
|
23359
23405
|
}
|
|
@@ -23427,7 +23473,7 @@ var registerMemoryEditTool = (server) => {
|
|
|
23427
23473
|
if (embedding)
|
|
23428
23474
|
store.updateEmbedding(result.memory.id, embedding, provider.name);
|
|
23429
23475
|
try {
|
|
23430
|
-
|
|
23476
|
+
refreshLinks(result.memory.id, requestInfo.agentId, result.memory.content);
|
|
23431
23477
|
} catch (err) {
|
|
23432
23478
|
console.error(`[memory-edit] Link resolution failed for ${result.memory.id}:`, err.message);
|
|
23433
23479
|
}
|
|
@@ -23464,12 +23510,99 @@ var registerMemoryEditTool = (server) => {
|
|
|
23464
23510
|
|
|
23465
23511
|
// src/tools/memory-get.ts
|
|
23466
23512
|
init_zod();
|
|
23513
|
+
init_db();
|
|
23467
23514
|
|
|
23468
23515
|
// src/be/memory/access.ts
|
|
23469
23516
|
function canReadMemory(memory, agentId) {
|
|
23470
23517
|
return memory.scope === "swarm" || memory.agentId === agentId;
|
|
23471
23518
|
}
|
|
23472
23519
|
|
|
23520
|
+
// src/be/memory/links-store.ts
|
|
23521
|
+
init_db();
|
|
23522
|
+
function redactedTargetId(sourceText) {
|
|
23523
|
+
const wikilink = /^\[\[(.+)\]\]$/.exec(sourceText ?? "");
|
|
23524
|
+
return wikilink?.[1] ?? "";
|
|
23525
|
+
}
|
|
23526
|
+
function getLinksForMemory(memoryId, options = {}) {
|
|
23527
|
+
const { viewerAgentId, isLead = false } = options;
|
|
23528
|
+
const db = getDb();
|
|
23529
|
+
const outgoing = db.prepare(`SELECT ml.id, ml.linkType, ml.targetKind, ml.targetId, ml.strength,
|
|
23530
|
+
ml.resolver, ml.sourceText, ml.createdAt,
|
|
23531
|
+
m.id AS targetMemoryId, m.name AS targetName,
|
|
23532
|
+
m.scope AS targetScope, m.agentId AS targetAgentId
|
|
23533
|
+
FROM memory_link ml
|
|
23534
|
+
LEFT JOIN agent_memory m
|
|
23535
|
+
ON ml.targetKind = 'memory'
|
|
23536
|
+
AND m.id = ml.targetId
|
|
23537
|
+
AND (m.expiresAt IS NULL OR m.expiresAt > datetime('now'))
|
|
23538
|
+
WHERE ml.from_memory_id = ?
|
|
23539
|
+
ORDER BY ml.createdAt ASC, ml.id ASC`).all(memoryId);
|
|
23540
|
+
const links = outgoing.map((row) => {
|
|
23541
|
+
if (row.targetKind !== "memory") {
|
|
23542
|
+
return {
|
|
23543
|
+
id: row.id,
|
|
23544
|
+
linkType: row.linkType,
|
|
23545
|
+
targetKind: row.targetKind,
|
|
23546
|
+
targetId: row.targetId,
|
|
23547
|
+
strength: row.strength,
|
|
23548
|
+
resolver: row.resolver,
|
|
23549
|
+
sourceText: row.sourceText,
|
|
23550
|
+
createdAt: row.createdAt,
|
|
23551
|
+
resolved: true
|
|
23552
|
+
};
|
|
23553
|
+
}
|
|
23554
|
+
const live = row.targetMemoryId !== null;
|
|
23555
|
+
const visible = live && (isLead || row.targetScope === "swarm" || (row.targetAgentId ?? "") === viewerAgentId);
|
|
23556
|
+
const base = {
|
|
23557
|
+
id: row.id,
|
|
23558
|
+
linkType: row.linkType,
|
|
23559
|
+
targetKind: row.targetKind,
|
|
23560
|
+
targetId: visible ? row.targetId : redactedTargetId(row.sourceText),
|
|
23561
|
+
strength: row.strength,
|
|
23562
|
+
resolver: row.resolver,
|
|
23563
|
+
sourceText: row.sourceText,
|
|
23564
|
+
createdAt: row.createdAt,
|
|
23565
|
+
resolved: false
|
|
23566
|
+
};
|
|
23567
|
+
if (!visible)
|
|
23568
|
+
return base;
|
|
23569
|
+
return {
|
|
23570
|
+
...base,
|
|
23571
|
+
resolved: true,
|
|
23572
|
+
target: {
|
|
23573
|
+
id: row.targetMemoryId,
|
|
23574
|
+
name: row.targetName ?? "",
|
|
23575
|
+
scope: row.targetScope ?? ""
|
|
23576
|
+
}
|
|
23577
|
+
};
|
|
23578
|
+
});
|
|
23579
|
+
const conditions = [
|
|
23580
|
+
"ml.targetKind = 'memory'",
|
|
23581
|
+
"ml.targetId = ?",
|
|
23582
|
+
"(m.expiresAt IS NULL OR m.expiresAt > datetime('now'))"
|
|
23583
|
+
];
|
|
23584
|
+
const params = [memoryId];
|
|
23585
|
+
if (!isLead) {
|
|
23586
|
+
conditions.push("(m.agentId = ? OR m.scope = 'swarm')");
|
|
23587
|
+
params.push(viewerAgentId ?? null);
|
|
23588
|
+
}
|
|
23589
|
+
const inbound = db.prepare(`SELECT ml.id, ml.linkType, ml.strength, ml.sourceText, ml.createdAt,
|
|
23590
|
+
m.id AS fromId, m.name AS fromName, m.scope AS fromScope
|
|
23591
|
+
FROM memory_link ml
|
|
23592
|
+
JOIN agent_memory m ON m.id = ml.from_memory_id
|
|
23593
|
+
WHERE ${conditions.join(" AND ")}
|
|
23594
|
+
ORDER BY ml.createdAt ASC, ml.id ASC`).all(...params);
|
|
23595
|
+
const backlinks = inbound.map((row) => ({
|
|
23596
|
+
id: row.id,
|
|
23597
|
+
linkType: row.linkType,
|
|
23598
|
+
strength: row.strength,
|
|
23599
|
+
sourceText: row.sourceText,
|
|
23600
|
+
createdAt: row.createdAt,
|
|
23601
|
+
from: { id: row.fromId, name: row.fromName, scope: row.fromScope }
|
|
23602
|
+
}));
|
|
23603
|
+
return { links, backlinks };
|
|
23604
|
+
}
|
|
23605
|
+
|
|
23473
23606
|
// src/be/memory/raters/retrieval.ts
|
|
23474
23607
|
init_db();
|
|
23475
23608
|
function recordRetrievals(taskId, agentId, results, sessionId, extras) {
|
|
@@ -23508,6 +23641,31 @@ function getRetrievalsForTask(taskId) {
|
|
|
23508
23641
|
// src/tools/memory-get.ts
|
|
23509
23642
|
init_types();
|
|
23510
23643
|
var NUDGE_ELIGIBLE_SOURCES = new Set(["manual", "file_index"]);
|
|
23644
|
+
var LinkedMemoryRefSchema = object({
|
|
23645
|
+
id: string(),
|
|
23646
|
+
name: string(),
|
|
23647
|
+
scope: string()
|
|
23648
|
+
});
|
|
23649
|
+
var MemoryLinkSchema = object({
|
|
23650
|
+
id: string(),
|
|
23651
|
+
linkType: string(),
|
|
23652
|
+
targetKind: string(),
|
|
23653
|
+
targetId: string(),
|
|
23654
|
+
strength: number(),
|
|
23655
|
+
resolver: string(),
|
|
23656
|
+
sourceText: string().nullable(),
|
|
23657
|
+
createdAt: string(),
|
|
23658
|
+
resolved: boolean().describe("For memory-kind targets: whether targetId points at a live memory you may read. Non-memory kinds (pr, agent-fs-file, …) are always resolved."),
|
|
23659
|
+
target: LinkedMemoryRefSchema.optional().describe("Linked memory metadata — present only for resolved memory-kind links.")
|
|
23660
|
+
});
|
|
23661
|
+
var MemoryBacklinkSchema = object({
|
|
23662
|
+
id: string(),
|
|
23663
|
+
linkType: string(),
|
|
23664
|
+
strength: number(),
|
|
23665
|
+
sourceText: string().nullable(),
|
|
23666
|
+
createdAt: string(),
|
|
23667
|
+
from: LinkedMemoryRefSchema.describe("The memory whose content links here.")
|
|
23668
|
+
});
|
|
23511
23669
|
var registerMemoryGetTool = (server) => {
|
|
23512
23670
|
createToolRegistrar(server)("memory-get", {
|
|
23513
23671
|
title: "Get memory details",
|
|
@@ -23522,6 +23680,8 @@ var registerMemoryGetTool = (server) => {
|
|
|
23522
23680
|
success: boolean(),
|
|
23523
23681
|
message: string(),
|
|
23524
23682
|
memory: AgentMemorySchema.optional(),
|
|
23683
|
+
links: array(MemoryLinkSchema).optional().describe("Outgoing memory_link rows resolved from this memory's content."),
|
|
23684
|
+
backlinks: array(MemoryBacklinkSchema).optional().describe("Other memories whose content links to this one (ACL-filtered)."),
|
|
23525
23685
|
rateHint: string().optional()
|
|
23526
23686
|
})
|
|
23527
23687
|
}, async ({ memoryId, intent }, requestInfo, _meta) => {
|
|
@@ -23557,13 +23717,26 @@ var registerMemoryGetTool = (server) => {
|
|
|
23557
23717
|
}
|
|
23558
23718
|
const inTaskContext = !!requestInfo.sourceTaskId;
|
|
23559
23719
|
const rateHint = inTaskContext && NUDGE_ELIGIBLE_SOURCES.has(memory.source) ? `memory_rate(id="${memory.id}", useful=true|false)` : undefined;
|
|
23720
|
+
let linkBlocks = { links: [], backlinks: [] };
|
|
23721
|
+
try {
|
|
23722
|
+
const agent = requestInfo.agentId ? getAgentById(requestInfo.agentId) : undefined;
|
|
23723
|
+
linkBlocks = getLinksForMemory(memory.id, {
|
|
23724
|
+
viewerAgentId: requestInfo.agentId,
|
|
23725
|
+
isLead: agent?.isLead ?? false
|
|
23726
|
+
});
|
|
23727
|
+
} catch (err) {
|
|
23728
|
+
console.error("[memory-get] link traversal failed:", err.message);
|
|
23729
|
+
}
|
|
23730
|
+
const linksSummary = linkBlocks.links.length > 0 || linkBlocks.backlinks.length > 0 ? `
|
|
23731
|
+
|
|
23732
|
+
[${linkBlocks.links.length} outgoing link(s), ${linkBlocks.backlinks.length} backlink(s) — see structured output]` : "";
|
|
23560
23733
|
return {
|
|
23561
23734
|
content: [
|
|
23562
23735
|
{
|
|
23563
23736
|
type: "text",
|
|
23564
23737
|
text: `Memory "${memory.name}" retrieved.
|
|
23565
23738
|
|
|
23566
|
-
${memory.content}`
|
|
23739
|
+
${memory.content}${linksSummary}`
|
|
23567
23740
|
}
|
|
23568
23741
|
],
|
|
23569
23742
|
structuredContent: {
|
|
@@ -23571,6 +23744,8 @@ ${memory.content}`
|
|
|
23571
23744
|
success: true,
|
|
23572
23745
|
message: `Memory "${memory.name}" retrieved.`,
|
|
23573
23746
|
memory,
|
|
23747
|
+
links: linkBlocks.links,
|
|
23748
|
+
backlinks: linkBlocks.backlinks,
|
|
23574
23749
|
rateHint
|
|
23575
23750
|
}
|
|
23576
23751
|
};
|
|
@@ -23688,6 +23863,103 @@ var registerMemoryRateTool = (server) => {
|
|
|
23688
23863
|
init_zod();
|
|
23689
23864
|
init_db();
|
|
23690
23865
|
init_constants();
|
|
23866
|
+
|
|
23867
|
+
// src/be/memory/graph-expansion.ts
|
|
23868
|
+
init_db();
|
|
23869
|
+
init_constants();
|
|
23870
|
+
init_sqlite_store();
|
|
23871
|
+
init_reranker();
|
|
23872
|
+
function addNeighborScopeConditions(conditions, params, agentId, scope, isLead) {
|
|
23873
|
+
if (!isLead) {
|
|
23874
|
+
if (scope === "agent") {
|
|
23875
|
+
conditions.push("m.agentId = ? AND m.scope = 'agent'");
|
|
23876
|
+
params.push(agentId);
|
|
23877
|
+
} else if (scope === "swarm") {
|
|
23878
|
+
conditions.push("m.scope = 'swarm'");
|
|
23879
|
+
} else {
|
|
23880
|
+
conditions.push("(m.agentId = ? OR m.scope = 'swarm')");
|
|
23881
|
+
params.push(agentId);
|
|
23882
|
+
}
|
|
23883
|
+
} else {
|
|
23884
|
+
if (scope === "agent") {
|
|
23885
|
+
conditions.push("m.scope = 'agent'");
|
|
23886
|
+
} else if (scope === "swarm") {
|
|
23887
|
+
conditions.push("m.scope = 'swarm'");
|
|
23888
|
+
}
|
|
23889
|
+
}
|
|
23890
|
+
}
|
|
23891
|
+
function expandCandidatesWithGraph(candidates, agentId, options = {}) {
|
|
23892
|
+
if (!isGraphExpansionEnabled())
|
|
23893
|
+
return candidates;
|
|
23894
|
+
const { cap = 5, damping = 0.7, scope = "all", source, isLead = false } = options;
|
|
23895
|
+
if (candidates.length === 0 || cap <= 0)
|
|
23896
|
+
return candidates;
|
|
23897
|
+
const parentById = new Map(candidates.map((c) => [c.id, c]));
|
|
23898
|
+
const parentIds = [...parentById.keys()];
|
|
23899
|
+
const conditions = [
|
|
23900
|
+
`ml.from_memory_id IN (${parentIds.map(() => "?").join(", ")})`,
|
|
23901
|
+
"ml.targetKind = 'memory'",
|
|
23902
|
+
"(m.expiresAt IS NULL OR m.expiresAt > datetime('now'))"
|
|
23903
|
+
];
|
|
23904
|
+
const params = [...parentIds];
|
|
23905
|
+
addNeighborScopeConditions(conditions, params, agentId, scope, isLead);
|
|
23906
|
+
if (source) {
|
|
23907
|
+
conditions.push("m.source = ?");
|
|
23908
|
+
params.push(source);
|
|
23909
|
+
}
|
|
23910
|
+
let rows;
|
|
23911
|
+
try {
|
|
23912
|
+
rows = getDb().prepare(`SELECT m.*, ml.from_memory_id AS fromMemoryId, ml.strength AS linkStrength
|
|
23913
|
+
FROM memory_link ml
|
|
23914
|
+
JOIN agent_memory m ON m.id = ml.targetId
|
|
23915
|
+
WHERE ${conditions.join(" AND ")}`).all(...params);
|
|
23916
|
+
} catch (err) {
|
|
23917
|
+
console.warn("[memory-graph] expansion query failed:", err.message);
|
|
23918
|
+
return candidates;
|
|
23919
|
+
}
|
|
23920
|
+
if (rows.length === 0)
|
|
23921
|
+
return candidates;
|
|
23922
|
+
const neighbors = new Map;
|
|
23923
|
+
for (const row of rows) {
|
|
23924
|
+
const parent = parentById.get(row.fromMemoryId);
|
|
23925
|
+
if (!parent)
|
|
23926
|
+
continue;
|
|
23927
|
+
const strength = typeof row.linkStrength === "number" ? row.linkStrength : 1;
|
|
23928
|
+
const parentBase = parent.rawSimilarity ?? parent.similarity;
|
|
23929
|
+
const similarity = parentBase * strength * damping;
|
|
23930
|
+
const existing = neighbors.get(row.id);
|
|
23931
|
+
if (existing && existing.similarity >= similarity)
|
|
23932
|
+
continue;
|
|
23933
|
+
neighbors.set(row.id, {
|
|
23934
|
+
...rowToCandidate(row, similarity),
|
|
23935
|
+
retrievalSource: "graph",
|
|
23936
|
+
recencyDecayApplied: false
|
|
23937
|
+
});
|
|
23938
|
+
}
|
|
23939
|
+
if (neighbors.size === 0)
|
|
23940
|
+
return candidates;
|
|
23941
|
+
const result = [...candidates];
|
|
23942
|
+
const indexById = new Map(result.map((c, i) => [c.id, i]));
|
|
23943
|
+
let added = 0;
|
|
23944
|
+
const now = new Date;
|
|
23945
|
+
const ranked = [...neighbors.values()].sort((a, b) => computeScore(b, now) - computeScore(a, now));
|
|
23946
|
+
for (const neighbor of ranked) {
|
|
23947
|
+
const existingIndex = indexById.get(neighbor.id);
|
|
23948
|
+
if (existingIndex !== undefined) {
|
|
23949
|
+
if (computeScore(neighbor, now) > computeScore(result[existingIndex], now)) {
|
|
23950
|
+
result[existingIndex] = neighbor;
|
|
23951
|
+
}
|
|
23952
|
+
continue;
|
|
23953
|
+
}
|
|
23954
|
+
if (added >= cap)
|
|
23955
|
+
continue;
|
|
23956
|
+
result.push(neighbor);
|
|
23957
|
+
added += 1;
|
|
23958
|
+
}
|
|
23959
|
+
return result;
|
|
23960
|
+
}
|
|
23961
|
+
|
|
23962
|
+
// src/tools/memory-search.ts
|
|
23691
23963
|
init_reranker();
|
|
23692
23964
|
init_types();
|
|
23693
23965
|
var NUDGE_ELIGIBLE_SOURCES2 = new Set(["manual", "file_index"]);
|
|
@@ -23717,7 +23989,8 @@ var registerMemorySearchTool = (server) => {
|
|
|
23717
23989
|
source: AgentMemorySourceSchema,
|
|
23718
23990
|
scope: AgentMemoryScopeSchema,
|
|
23719
23991
|
similarity: number().optional(),
|
|
23720
|
-
retrievalSource: _enum(["vec", "fts", "hybrid", "fallback"]).optional(),
|
|
23992
|
+
retrievalSource: _enum(["vec", "fts", "hybrid", "fallback", "graph"]).optional(),
|
|
23993
|
+
tags: array(string()).optional(),
|
|
23721
23994
|
createdAt: string(),
|
|
23722
23995
|
rateHint: string().optional()
|
|
23723
23996
|
})).optional(),
|
|
@@ -23747,8 +24020,13 @@ var registerMemorySearchTool = (server) => {
|
|
|
23747
24020
|
isLead,
|
|
23748
24021
|
queryText: query
|
|
23749
24022
|
});
|
|
23750
|
-
|
|
23751
|
-
|
|
24023
|
+
const expanded = expandCandidatesWithGraph(candidates, requestInfo.agentId, {
|
|
24024
|
+
scope,
|
|
24025
|
+
source,
|
|
24026
|
+
isLead
|
|
24027
|
+
});
|
|
24028
|
+
if (expanded.length > 0) {
|
|
24029
|
+
const ranked = rerank(expanded, { limit });
|
|
23752
24030
|
if (requestInfo.sourceTaskId) {
|
|
23753
24031
|
try {
|
|
23754
24032
|
recordRetrievals(requestInfo.sourceTaskId, requestInfo.agentId, ranked.map((r) => ({
|
|
@@ -23769,6 +24047,7 @@ var registerMemorySearchTool = (server) => {
|
|
|
23769
24047
|
scope: r.scope,
|
|
23770
24048
|
similarity: r.similarity,
|
|
23771
24049
|
retrievalSource: r.retrievalSource,
|
|
24050
|
+
tags: r.tags,
|
|
23772
24051
|
createdAt: r.createdAt,
|
|
23773
24052
|
...inTaskContext && NUDGE_ELIGIBLE_SOURCES2.has(r.source) ? { rateHint: rateHintFor(r.id) } : {}
|
|
23774
24053
|
}));
|
|
@@ -23802,6 +24081,7 @@ var registerMemorySearchTool = (server) => {
|
|
|
23802
24081
|
summary: r.summary,
|
|
23803
24082
|
source: r.source,
|
|
23804
24083
|
scope: r.scope,
|
|
24084
|
+
tags: r.tags,
|
|
23805
24085
|
createdAt: r.createdAt
|
|
23806
24086
|
}));
|
|
23807
24087
|
return {
|
|
@@ -28663,6 +28943,87 @@ var registerSkillUpdateTool = (server) => {
|
|
|
28663
28943
|
}
|
|
28664
28944
|
});
|
|
28665
28945
|
};
|
|
28946
|
+
// src/tools/slack-delete.ts
|
|
28947
|
+
init_zod();
|
|
28948
|
+
init_db();
|
|
28949
|
+
var registerSlackDeleteTool = (server) => {
|
|
28950
|
+
createToolRegistrar(server)("slack-delete", {
|
|
28951
|
+
title: "Delete a Slack message",
|
|
28952
|
+
description: "Deletes a Slack message that THIS bot authored (e.g. a message previously posted via `slack-post`/`slack-reply`). Cannot delete messages authored by humans or other apps. Requires lead privileges.",
|
|
28953
|
+
annotations: { openWorldHint: true, destructiveHint: true },
|
|
28954
|
+
inputSchema: object({
|
|
28955
|
+
channelId: string().min(1).describe("The Slack channel ID the message is in."),
|
|
28956
|
+
messageTs: string().min(1).describe("Timestamp of the message to delete. Accepts the dotted form (1783411554.596189), the 'p' deep-link form (p1783411554596189), or a full Slack permalink URL.")
|
|
28957
|
+
}),
|
|
28958
|
+
outputSchema: object({
|
|
28959
|
+
success: boolean(),
|
|
28960
|
+
message: string()
|
|
28961
|
+
})
|
|
28962
|
+
}, async ({ channelId, messageTs }, requestInfo, _meta) => {
|
|
28963
|
+
if (!requestInfo.agentId) {
|
|
28964
|
+
return {
|
|
28965
|
+
content: [{ type: "text", text: "Agent ID not found." }],
|
|
28966
|
+
structuredContent: { success: false, message: "Agent ID not found." }
|
|
28967
|
+
};
|
|
28968
|
+
}
|
|
28969
|
+
const agent = getAgentById(requestInfo.agentId);
|
|
28970
|
+
if (!agent) {
|
|
28971
|
+
return {
|
|
28972
|
+
content: [{ type: "text", text: "Agent not found." }],
|
|
28973
|
+
structuredContent: { success: false, message: "Agent not found." }
|
|
28974
|
+
};
|
|
28975
|
+
}
|
|
28976
|
+
if (!agent.isLead) {
|
|
28977
|
+
return {
|
|
28978
|
+
content: [{ type: "text", text: "Deleting Slack messages requires lead privileges." }],
|
|
28979
|
+
structuredContent: {
|
|
28980
|
+
success: false,
|
|
28981
|
+
message: "Deleting Slack messages requires lead privileges."
|
|
28982
|
+
}
|
|
28983
|
+
};
|
|
28984
|
+
}
|
|
28985
|
+
const app = getSlackApp();
|
|
28986
|
+
if (!app) {
|
|
28987
|
+
return {
|
|
28988
|
+
content: [{ type: "text", text: "Slack not configured." }],
|
|
28989
|
+
structuredContent: { success: false, message: "Slack not configured." }
|
|
28990
|
+
};
|
|
28991
|
+
}
|
|
28992
|
+
try {
|
|
28993
|
+
const ts = parseSlackTs(messageTs);
|
|
28994
|
+
await app.client.chat.delete({ channel: channelId, ts });
|
|
28995
|
+
return {
|
|
28996
|
+
content: [{ type: "text", text: "Message deleted successfully." }],
|
|
28997
|
+
structuredContent: { success: true, message: "Message deleted successfully." }
|
|
28998
|
+
};
|
|
28999
|
+
} catch (error) {
|
|
29000
|
+
const errorCode = error?.data?.error;
|
|
29001
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
29002
|
+
let message;
|
|
29003
|
+
switch (errorCode) {
|
|
29004
|
+
case "message_not_found":
|
|
29005
|
+
message = "No message found at that timestamp in this channel.";
|
|
29006
|
+
break;
|
|
29007
|
+
case "cant_delete_message":
|
|
29008
|
+
message = "Cannot delete this message — the bot can only delete messages it authored.";
|
|
29009
|
+
break;
|
|
29010
|
+
case "channel_not_found":
|
|
29011
|
+
message = "Channel not found or the bot has no access.";
|
|
29012
|
+
break;
|
|
29013
|
+
case "not_in_channel":
|
|
29014
|
+
message = "The bot is not in that channel.";
|
|
29015
|
+
break;
|
|
29016
|
+
default:
|
|
29017
|
+
message = `Failed to delete message: ${errorMsg}`;
|
|
29018
|
+
}
|
|
29019
|
+
return {
|
|
29020
|
+
content: [{ type: "text", text: message }],
|
|
29021
|
+
structuredContent: { success: false, message }
|
|
29022
|
+
};
|
|
29023
|
+
}
|
|
29024
|
+
});
|
|
29025
|
+
};
|
|
29026
|
+
|
|
28666
29027
|
// src/tools/slack-download-file.ts
|
|
28667
29028
|
init_zod();
|
|
28668
29029
|
init_db();
|
|
@@ -29476,7 +29837,8 @@ var registerSlackReplyTool = (server) => {
|
|
|
29476
29837
|
}),
|
|
29477
29838
|
outputSchema: object({
|
|
29478
29839
|
success: boolean(),
|
|
29479
|
-
message: string()
|
|
29840
|
+
message: string(),
|
|
29841
|
+
messageTs: string().optional()
|
|
29480
29842
|
})
|
|
29481
29843
|
}, async ({ inboxMessageId, taskId, message }, requestInfo, _meta) => {
|
|
29482
29844
|
if (!requestInfo.agentId) {
|
|
@@ -29548,7 +29910,7 @@ var registerSlackReplyTool = (server) => {
|
|
|
29548
29910
|
}
|
|
29549
29911
|
try {
|
|
29550
29912
|
const slackMessage = markdownToSlack(message);
|
|
29551
|
-
await withAutoJoin(app.client, slackChannelId, () => app.client.chat.postMessage({
|
|
29913
|
+
const result = await withAutoJoin(app.client, slackChannelId, () => app.client.chat.postMessage({
|
|
29552
29914
|
channel: slackChannelId,
|
|
29553
29915
|
thread_ts: slackThreadTs,
|
|
29554
29916
|
text: slackMessage,
|
|
@@ -29564,13 +29926,19 @@ var registerSlackReplyTool = (server) => {
|
|
|
29564
29926
|
}
|
|
29565
29927
|
]
|
|
29566
29928
|
}));
|
|
29929
|
+
const messageTs = result.ts;
|
|
29567
29930
|
if (taskId) {
|
|
29568
29931
|
markTaskSlackReplySent(taskId);
|
|
29569
29932
|
console.log(`[Slack] Marked slackReplySent=1 for task ${taskId}`);
|
|
29570
29933
|
}
|
|
29571
29934
|
return {
|
|
29572
|
-
content: [
|
|
29573
|
-
|
|
29935
|
+
content: [
|
|
29936
|
+
{
|
|
29937
|
+
type: "text",
|
|
29938
|
+
text: `Reply sent successfully.${messageTs ? ` Message timestamp: ${messageTs}` : ""}`
|
|
29939
|
+
}
|
|
29940
|
+
],
|
|
29941
|
+
structuredContent: { success: true, message: "Reply sent successfully.", messageTs }
|
|
29574
29942
|
};
|
|
29575
29943
|
} catch (error) {
|
|
29576
29944
|
return {
|
|
@@ -29597,7 +29965,8 @@ var registerSlackStartThreadTool = (server) => {
|
|
|
29597
29965
|
success: boolean(),
|
|
29598
29966
|
message: string(),
|
|
29599
29967
|
channelId: string().optional(),
|
|
29600
|
-
ts: string().optional()
|
|
29968
|
+
ts: string().optional(),
|
|
29969
|
+
messageTs: string().optional()
|
|
29601
29970
|
})
|
|
29602
29971
|
}, async ({ channelId, message }, requestInfo, _meta) => {
|
|
29603
29972
|
if (!requestInfo.agentId) {
|
|
@@ -29674,7 +30043,8 @@ var registerSlackStartThreadTool = (server) => {
|
|
|
29674
30043
|
success: true,
|
|
29675
30044
|
message: "Thread started successfully.",
|
|
29676
30045
|
channelId: resolvedChannelId,
|
|
29677
|
-
ts
|
|
30046
|
+
ts,
|
|
30047
|
+
messageTs: ts
|
|
29678
30048
|
}
|
|
29679
30049
|
};
|
|
29680
30050
|
} catch (error) {
|
|
@@ -29687,10 +30057,122 @@ var registerSlackStartThreadTool = (server) => {
|
|
|
29687
30057
|
});
|
|
29688
30058
|
};
|
|
29689
30059
|
|
|
30060
|
+
// src/tools/slack-update.ts
|
|
30061
|
+
init_zod();
|
|
30062
|
+
init_db();
|
|
30063
|
+
var registerSlackUpdateTool = (server) => {
|
|
30064
|
+
createToolRegistrar(server)("slack-update", {
|
|
30065
|
+
title: "Edit a Slack message",
|
|
30066
|
+
description: "Edits (in place) the text of a Slack message that THIS bot authored — use it to post corrections to your own messages. Cannot edit messages authored by humans or other apps. Note: editing may reset the message's display name/icon to the app default (Slack's chat.update cannot set the crown persona). Requires lead privileges.",
|
|
30067
|
+
annotations: { openWorldHint: true },
|
|
30068
|
+
inputSchema: object({
|
|
30069
|
+
channelId: string().min(1).describe("The Slack channel ID the message is in."),
|
|
30070
|
+
messageTs: string().min(1).describe("Timestamp of the message to edit (dotted, 'p' deep-link, or full permalink URL)."),
|
|
30071
|
+
message: string().min(1).max(4000).describe("The new message content.")
|
|
30072
|
+
}),
|
|
30073
|
+
outputSchema: object({
|
|
30074
|
+
success: boolean(),
|
|
30075
|
+
message: string(),
|
|
30076
|
+
messageTs: string().optional()
|
|
30077
|
+
})
|
|
30078
|
+
}, async ({ channelId, messageTs, message }, requestInfo, _meta) => {
|
|
30079
|
+
if (!requestInfo.agentId) {
|
|
30080
|
+
return {
|
|
30081
|
+
content: [{ type: "text", text: "Agent ID not found." }],
|
|
30082
|
+
structuredContent: { success: false, message: "Agent ID not found." }
|
|
30083
|
+
};
|
|
30084
|
+
}
|
|
30085
|
+
const agent = getAgentById(requestInfo.agentId);
|
|
30086
|
+
if (!agent) {
|
|
30087
|
+
return {
|
|
30088
|
+
content: [{ type: "text", text: "Agent not found." }],
|
|
30089
|
+
structuredContent: { success: false, message: "Agent not found." }
|
|
30090
|
+
};
|
|
30091
|
+
}
|
|
30092
|
+
if (!agent.isLead) {
|
|
30093
|
+
return {
|
|
30094
|
+
content: [{ type: "text", text: "Editing Slack messages requires lead privileges." }],
|
|
30095
|
+
structuredContent: {
|
|
30096
|
+
success: false,
|
|
30097
|
+
message: "Editing Slack messages requires lead privileges."
|
|
30098
|
+
}
|
|
30099
|
+
};
|
|
30100
|
+
}
|
|
30101
|
+
const app = getSlackApp();
|
|
30102
|
+
if (!app) {
|
|
30103
|
+
return {
|
|
30104
|
+
content: [{ type: "text", text: "Slack not configured." }],
|
|
30105
|
+
structuredContent: { success: false, message: "Slack not configured." }
|
|
30106
|
+
};
|
|
30107
|
+
}
|
|
30108
|
+
try {
|
|
30109
|
+
const ts = parseSlackTs(messageTs);
|
|
30110
|
+
const slackMessage = markdownToSlack(message);
|
|
30111
|
+
const result = await app.client.chat.update({
|
|
30112
|
+
channel: channelId,
|
|
30113
|
+
ts,
|
|
30114
|
+
text: slackMessage,
|
|
30115
|
+
blocks: [
|
|
30116
|
+
{
|
|
30117
|
+
type: "section",
|
|
30118
|
+
text: {
|
|
30119
|
+
type: "mrkdwn",
|
|
30120
|
+
text: slackMessage
|
|
30121
|
+
}
|
|
30122
|
+
}
|
|
30123
|
+
]
|
|
30124
|
+
});
|
|
30125
|
+
return {
|
|
30126
|
+
content: [{ type: "text", text: "Message updated successfully." }],
|
|
30127
|
+
structuredContent: {
|
|
30128
|
+
success: true,
|
|
30129
|
+
message: "Message updated successfully.",
|
|
30130
|
+
messageTs: result.ts
|
|
30131
|
+
}
|
|
30132
|
+
};
|
|
30133
|
+
} catch (error) {
|
|
30134
|
+
const errorCode = error?.data?.error;
|
|
30135
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
30136
|
+
let message2;
|
|
30137
|
+
switch (errorCode) {
|
|
30138
|
+
case "message_not_found":
|
|
30139
|
+
message2 = "No message found at that timestamp in this channel.";
|
|
30140
|
+
break;
|
|
30141
|
+
case "cant_update_message":
|
|
30142
|
+
message2 = "Cannot edit this message — the bot can only edit messages it authored.";
|
|
30143
|
+
break;
|
|
30144
|
+
case "edit_window_closed":
|
|
30145
|
+
message2 = "The edit window for this message has closed.";
|
|
30146
|
+
break;
|
|
30147
|
+
case "channel_not_found":
|
|
30148
|
+
message2 = "Channel not found or the bot has no access.";
|
|
30149
|
+
break;
|
|
30150
|
+
case "not_in_channel":
|
|
30151
|
+
message2 = "The bot is not in that channel.";
|
|
30152
|
+
break;
|
|
30153
|
+
default:
|
|
30154
|
+
message2 = `Failed to update message: ${errorMsg}`;
|
|
30155
|
+
}
|
|
30156
|
+
return {
|
|
30157
|
+
content: [{ type: "text", text: message2 }],
|
|
30158
|
+
structuredContent: { success: false, message: message2 }
|
|
30159
|
+
};
|
|
30160
|
+
}
|
|
30161
|
+
});
|
|
30162
|
+
};
|
|
30163
|
+
|
|
29690
30164
|
// src/tools/slack-upload-file.ts
|
|
29691
30165
|
init_zod();
|
|
29692
30166
|
init_db();
|
|
29693
30167
|
var WORKSPACE_DIR = "/workspace";
|
|
30168
|
+
function resolveTaskUploadThreadTs(task) {
|
|
30169
|
+
if (!task)
|
|
30170
|
+
return;
|
|
30171
|
+
if (task.slackChannelId?.startsWith("D")) {
|
|
30172
|
+
return task.slackTreeRootMessageTs ?? task.slackProgressMessageTs ?? task.slackThreadTs;
|
|
30173
|
+
}
|
|
30174
|
+
return task.slackThreadTs;
|
|
30175
|
+
}
|
|
29694
30176
|
async function resolveAndValidateFilePath(filePath) {
|
|
29695
30177
|
const triedPaths = [];
|
|
29696
30178
|
const fileExists = async (path2) => {
|
|
@@ -29798,7 +30280,7 @@ var registerSlackUploadFileTool = (server) => {
|
|
|
29798
30280
|
};
|
|
29799
30281
|
}
|
|
29800
30282
|
slackChannelId = task.slackChannelId;
|
|
29801
|
-
slackThreadTs = task
|
|
30283
|
+
slackThreadTs = resolveTaskUploadThreadTs(task);
|
|
29802
30284
|
} else if (channelId) {
|
|
29803
30285
|
if (!agent.isLead) {
|
|
29804
30286
|
return {
|
|
@@ -32021,24 +32503,25 @@ function parseNextPageLink(linkHeader) {
|
|
|
32021
32503
|
const match = linkHeader.match(/<([^>]+)>;\s*rel="next"/);
|
|
32022
32504
|
return match ? match[1] ?? null : null;
|
|
32023
32505
|
}
|
|
32024
|
-
|
|
32025
|
-
|
|
32026
|
-
|
|
32027
|
-
|
|
32028
|
-
|
|
32029
|
-
const headers = {
|
|
32506
|
+
var REVIEW_COMMENTS_EMPTY_RETRY_DELAYS_MS = [1500, 3000, 3000];
|
|
32507
|
+
var defaultReviewCommentsRetryDelay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
32508
|
+
var reviewCommentsRetryDelay = defaultReviewCommentsRetryDelay;
|
|
32509
|
+
function buildReviewCommentsHeaders(token) {
|
|
32510
|
+
return {
|
|
32030
32511
|
Accept: "application/vnd.github+json",
|
|
32031
32512
|
Authorization: `Bearer ${token}`,
|
|
32032
32513
|
"X-GitHub-Api-Version": "2022-11-28"
|
|
32033
32514
|
};
|
|
32515
|
+
}
|
|
32516
|
+
async function fetchPaginatedReviewComments(initialUrl, headers) {
|
|
32034
32517
|
const allComments = [];
|
|
32035
|
-
let url =
|
|
32518
|
+
let url = initialUrl;
|
|
32036
32519
|
try {
|
|
32037
32520
|
while (url) {
|
|
32038
32521
|
const response = await fetch(url, { headers });
|
|
32039
32522
|
if (!response.ok) {
|
|
32040
32523
|
console.error(`[GitHub] Failed to fetch review inline comments: ${response.status}`);
|
|
32041
|
-
return allComments;
|
|
32524
|
+
return { comments: allComments, degraded: true };
|
|
32042
32525
|
}
|
|
32043
32526
|
const page = await response.json();
|
|
32044
32527
|
if (Array.isArray(page)) {
|
|
@@ -32046,11 +32529,66 @@ async function fetchReviewComments(repo, prNumber, reviewId, installationId) {
|
|
|
32046
32529
|
}
|
|
32047
32530
|
url = parseNextPageLink(response.headers.get("link"));
|
|
32048
32531
|
}
|
|
32049
|
-
return allComments;
|
|
32532
|
+
return { comments: allComments, degraded: false };
|
|
32050
32533
|
} catch (error) {
|
|
32051
32534
|
console.error("[GitHub] Error fetching review inline comments:", error);
|
|
32052
|
-
return allComments;
|
|
32535
|
+
return { comments: allComments, degraded: true };
|
|
32536
|
+
}
|
|
32537
|
+
}
|
|
32538
|
+
async function fetchReviewScopedComments(repo, prNumber, reviewId, headers) {
|
|
32539
|
+
const url = `https://api.github.com/repos/${repo}/pulls/${prNumber}/reviews/${reviewId}/comments?per_page=100`;
|
|
32540
|
+
let result = await fetchPaginatedReviewComments(url, headers);
|
|
32541
|
+
if (result.degraded || result.comments.length > 0) {
|
|
32542
|
+
return result;
|
|
32543
|
+
}
|
|
32544
|
+
for (const delayMs of REVIEW_COMMENTS_EMPTY_RETRY_DELAYS_MS) {
|
|
32545
|
+
await reviewCommentsRetryDelay(delayMs);
|
|
32546
|
+
result = await fetchPaginatedReviewComments(url, headers);
|
|
32547
|
+
if (result.degraded || result.comments.length > 0) {
|
|
32548
|
+
return result;
|
|
32549
|
+
}
|
|
32550
|
+
}
|
|
32551
|
+
return result;
|
|
32552
|
+
}
|
|
32553
|
+
async function fetchPrLevelReviewComments(repo, prNumber, reviewId, headers) {
|
|
32554
|
+
const url = `https://api.github.com/repos/${repo}/pulls/${prNumber}/comments?per_page=100`;
|
|
32555
|
+
const result = await fetchPaginatedReviewComments(url, headers);
|
|
32556
|
+
return {
|
|
32557
|
+
comments: result.comments.filter((comment) => comment.pull_request_review_id === reviewId),
|
|
32558
|
+
degraded: result.degraded
|
|
32559
|
+
};
|
|
32560
|
+
}
|
|
32561
|
+
function dedupeReviewComments(comments) {
|
|
32562
|
+
const byId = new Map;
|
|
32563
|
+
for (const comment of comments) {
|
|
32564
|
+
if (!byId.has(comment.id)) {
|
|
32565
|
+
byId.set(comment.id, comment);
|
|
32566
|
+
}
|
|
32053
32567
|
}
|
|
32568
|
+
return [...byId.values()];
|
|
32569
|
+
}
|
|
32570
|
+
async function fetchReviewComments(repo, prNumber, reviewId, installationId) {
|
|
32571
|
+
const token = await getInstallationToken(installationId);
|
|
32572
|
+
if (!token) {
|
|
32573
|
+
return { comments: [], degraded: true };
|
|
32574
|
+
}
|
|
32575
|
+
const headers = buildReviewCommentsHeaders(token);
|
|
32576
|
+
const scopedResult = await fetchReviewScopedComments(repo, prNumber, reviewId, headers);
|
|
32577
|
+
if (!scopedResult.degraded && scopedResult.comments.length > 0) {
|
|
32578
|
+
return scopedResult;
|
|
32579
|
+
}
|
|
32580
|
+
const fallbackResult = await fetchPrLevelReviewComments(repo, prNumber, reviewId, headers);
|
|
32581
|
+
const mergedComments = dedupeReviewComments([
|
|
32582
|
+
...scopedResult.comments,
|
|
32583
|
+
...fallbackResult.comments
|
|
32584
|
+
]);
|
|
32585
|
+
if (fallbackResult.comments.length > 0) {
|
|
32586
|
+
return { comments: mergedComments, degraded: false };
|
|
32587
|
+
}
|
|
32588
|
+
return {
|
|
32589
|
+
comments: mergedComments,
|
|
32590
|
+
degraded: scopedResult.degraded || fallbackResult.degraded
|
|
32591
|
+
};
|
|
32054
32592
|
}
|
|
32055
32593
|
function buildInlineCommentsSection(comments) {
|
|
32056
32594
|
if (comments.length === 0)
|
|
@@ -32072,6 +32610,14 @@ ${items.join(`
|
|
|
32072
32610
|
|
|
32073
32611
|
`)}`;
|
|
32074
32612
|
}
|
|
32613
|
+
function buildInlineCommentsDegradedSection(repo, prNumber) {
|
|
32614
|
+
return `
|
|
32615
|
+
|
|
32616
|
+
## ⚠️ Inline comments could NOT be auto-fetched
|
|
32617
|
+
The automatic inline-comment fetch failed or was unverifiable while the reviewer submitted this review. Inline comments ARE the change requests. BEFORE scoping or dispatching this task you MUST fetch them yourself:
|
|
32618
|
+
\`gh api "repos/${repo}/pulls/${prNumber}/comments?per_page=100" --jq '.[] | {id,path,line,body}'\`
|
|
32619
|
+
Reply to and resolve EVERY unresolved inline thread. Do NOT dispatch off the review body alone.`;
|
|
32620
|
+
}
|
|
32075
32621
|
async function handlePullRequestReview(event) {
|
|
32076
32622
|
const { action, review, pull_request: pr, repository, sender, installation } = event;
|
|
32077
32623
|
const requestedByUserId = resolveGitHubSender(sender.login, "pull_request_review", `Review on PR #${pr.number}: ${review.state}`);
|
|
@@ -32082,8 +32628,8 @@ async function handlePullRequestReview(event) {
|
|
|
32082
32628
|
if (isDuplicate2(eventKey)) {
|
|
32083
32629
|
return { created: false };
|
|
32084
32630
|
}
|
|
32085
|
-
const inlineComments = installation?.id ? await fetchReviewComments(repository.full_name, pr.number, review.id, installation.id) : [];
|
|
32086
|
-
if (review.state === "commented" && !review.body && inlineComments.length === 0) {
|
|
32631
|
+
const { comments: inlineComments, degraded } = installation?.id ? await fetchReviewComments(repository.full_name, pr.number, review.id, installation.id) : { comments: [], degraded: true };
|
|
32632
|
+
if (review.state === "commented" && !review.body && inlineComments.length === 0 && !degraded) {
|
|
32087
32633
|
return { created: false };
|
|
32088
32634
|
}
|
|
32089
32635
|
const existingTask = findTaskByVcs(repository.full_name, pr.number);
|
|
@@ -32097,13 +32643,13 @@ async function handlePullRequestReview(event) {
|
|
|
32097
32643
|
|
|
32098
32644
|
Review Comment:
|
|
32099
32645
|
${review.body}` : "";
|
|
32100
|
-
const inlineCommentsSection = buildInlineCommentsSection(inlineComments);
|
|
32646
|
+
const inlineCommentsSection = buildInlineCommentsSection(inlineComments) + (degraded ? buildInlineCommentsDegradedSection(repository.full_name, pr.number) : "");
|
|
32101
32647
|
const relatedTaskSection = existingTask ? `Related task: ${existingTask.id}
|
|
32102
32648
|
\uD83D\uDD00 Consider routing to the same agent working on the related task.
|
|
32103
32649
|
` : "";
|
|
32104
32650
|
const hasInlineComments = inlineComments.length > 0;
|
|
32105
32651
|
const baseReviewSuggestion = review.state === "approved" ? "\uD83D\uDCA1 Suggested: Merge the PR or wait for additional reviews" : review.state === "changes_requested" ? "\uD83D\uDCA1 Suggested: Address the requested changes and update the PR" : "\uD83D\uDCA1 Suggested: Review the feedback and respond if needed";
|
|
32106
|
-
const reviewSuggestions = hasInlineComments ? `${baseReviewSuggestion}
|
|
32652
|
+
const reviewSuggestions = hasInlineComments || degraded ? `${baseReviewSuggestion}
|
|
32107
32653
|
\uD83D\uDCAC Address EVERY inline comment. After pushing fixes, reply to and resolve each inline review thread on GitHub so the reviewer sees visible confirmation.` : baseReviewSuggestion;
|
|
32108
32654
|
const result = resolveTemplate("github.pull_request.review_submitted", {
|
|
32109
32655
|
review_emoji: emoji,
|
|
@@ -32884,7 +33430,7 @@ async function notifySlack(text) {
|
|
|
32884
33430
|
return;
|
|
32885
33431
|
}
|
|
32886
33432
|
try {
|
|
32887
|
-
const { getSlackApp: getSlackApp2 } = await import("./app-
|
|
33433
|
+
const { getSlackApp: getSlackApp2 } = await import("./app-b05sgm02.js");
|
|
32888
33434
|
const app = getSlackApp2();
|
|
32889
33435
|
if (!app) {
|
|
32890
33436
|
console.warn("[Jira webhook keepalive] Slack not available, cannot send notification");
|
|
@@ -32953,7 +33499,7 @@ async function deleteJiraWebhook(webhookId) {
|
|
|
32953
33499
|
console.log(`[Jira webhook keepalive] Deleted webhook id=${webhookId}`);
|
|
32954
33500
|
}
|
|
32955
33501
|
async function overwriteWebhookIds(next) {
|
|
32956
|
-
const { getDb: getDb2 } = await import("./db-
|
|
33502
|
+
const { getDb: getDb2 } = await import("./db-3tge4jrn.js");
|
|
32957
33503
|
const db = getDb2();
|
|
32958
33504
|
const txn = db.transaction(() => {
|
|
32959
33505
|
const row = db.query("SELECT metadata FROM oauth_apps WHERE provider = 'jira'").get();
|
|
@@ -110005,6 +110551,8 @@ function createServer() {
|
|
|
110005
110551
|
registerSlackListChannelsTool(server);
|
|
110006
110552
|
registerSlackUploadFileTool(server);
|
|
110007
110553
|
registerSlackDownloadFileTool(server);
|
|
110554
|
+
registerSlackDeleteTool(server);
|
|
110555
|
+
registerSlackUpdateTool(server);
|
|
110008
110556
|
registerRegisterAgentmailInboxTool(server);
|
|
110009
110557
|
registerRegisterKapsoNumberTool(server);
|
|
110010
110558
|
registerUnregisterKapsoNumberTool(server);
|
|
@@ -110240,4 +110788,4 @@ function writeSkillsToFilesystem(entries, harnessType = "all", home) {
|
|
|
110240
110788
|
return { synced, removed, errors };
|
|
110241
110789
|
}
|
|
110242
110790
|
|
|
110243
|
-
export { DEFAULT_NEGOTIATED_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, isJSONRPCRequest, isJSONRPCResultResponse, isJSONRPCErrorResponse, JSONRPCMessageSchema, isInitializeRequest, McpServer, isGitHubEnabled, initGitHub, verifyWebhookSignature, userCtx, isPollTracingEnabled, initOtel, withSpan, startSpan, withSpanContext, withRemoteContext, injectTraceContext, recordSessionCost, createToolRegistrar, cancelTaskInputSchema, cancelTaskOutputSchema, cancelTaskHandler, setCorsHeaders, parseQueryParams, getPathSegments, safeRequestUrlForLog, agentWithCapacity, parseBody, BODY_TOO_LARGE, enforceContentLengthCap, json, jsonError, triggerSchemaErrorResponse, deriveApiBaseUrl, httpServerSemconvAttributes, describeRequestRoute, route, assertSelectOnlyQuery, executeReadOnlyQuery, handleDbQuery, snapshotMetric, snapshotPage, getTaskDetailsInputSchema, getTaskDetailsOutputSchema, getTaskDetailsHandler, getTasksInputSchema, getTasksOutputSchema, getTasksHandler, storeLinks, canReadMemory, recordRetrievals, markKapsoMessageRead, sendKapsoReaction, getKapsoConfig, getKapsoNumberMapping, markKapsoMessageSeen, resolveHttpAuditUserId, createEvent, createEventsBatch, getEventCountsFiltered, getEventsFiltered, mergeScheduleTiming, validateRecurringTiming, sendTaskOutputSchema, sendTaskHandler, parseSkillContent, ExplicitSelfDuplicateError, applyRating, shouldPersistAutomaticTaskMemory, isAgentMailEnabled, initAgentMail, verifyAgentMailWebhook, isInboxAllowed, isSenderAllowed, handleMessageReceived, handlePullRequest, handleIssue, handleComment, handlePullRequestReview, handleCheckRun, handleCheckSuite, handleWorkflowRun, getJiraMetadata, updateJiraMetadata, clearJiraMetadata, registerJiraWebhook, deleteJiraWebhook, isJiraEnabled, initJira, handleJiraWebhook, handleAgentSessionEvent, handleIssueUpdate, handleIssueDelete, handleAgentSessionPrompted, isLinearEnabled, initLinear, loadGlobalConfigsIntoEnv, reloadGlobalConfigsAndIntegrations, scheduleIntegrationsReload, handleCore, canClaim, emitBudgetRefusalSideEffects, taskActionInputSchema, taskActionOutputSchema, taskActionHandler, hasCapability, getEnabledCapabilities, createServer, writeSkillsToFilesystem };
|
|
110791
|
+
export { DEFAULT_NEGOTIATED_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, isJSONRPCRequest, isJSONRPCResultResponse, isJSONRPCErrorResponse, JSONRPCMessageSchema, isInitializeRequest, McpServer, isGitHubEnabled, initGitHub, verifyWebhookSignature, userCtx, isPollTracingEnabled, initOtel, withSpan, startSpan, withSpanContext, withRemoteContext, injectTraceContext, recordSessionCost, createToolRegistrar, cancelTaskInputSchema, cancelTaskOutputSchema, cancelTaskHandler, setCorsHeaders, parseQueryParams, getPathSegments, safeRequestUrlForLog, agentWithCapacity, parseBody, BODY_TOO_LARGE, enforceContentLengthCap, json, jsonError, triggerSchemaErrorResponse, deriveApiBaseUrl, httpServerSemconvAttributes, describeRequestRoute, route, assertSelectOnlyQuery, executeReadOnlyQuery, handleDbQuery, snapshotMetric, snapshotPage, getTaskDetailsInputSchema, getTaskDetailsOutputSchema, getTaskDetailsHandler, getTasksInputSchema, getTasksOutputSchema, getTasksHandler, storeLinks, refreshLinks, canReadMemory, getLinksForMemory, recordRetrievals, expandCandidatesWithGraph, markKapsoMessageRead, sendKapsoReaction, getKapsoConfig, getKapsoNumberMapping, markKapsoMessageSeen, resolveHttpAuditUserId, createEvent, createEventsBatch, getEventCountsFiltered, getEventsFiltered, mergeScheduleTiming, validateRecurringTiming, sendTaskOutputSchema, sendTaskHandler, parseSkillContent, ExplicitSelfDuplicateError, applyRating, shouldPersistAutomaticTaskMemory, isAgentMailEnabled, initAgentMail, verifyAgentMailWebhook, isInboxAllowed, isSenderAllowed, handleMessageReceived, handlePullRequest, handleIssue, handleComment, handlePullRequestReview, handleCheckRun, handleCheckSuite, handleWorkflowRun, getJiraMetadata, updateJiraMetadata, clearJiraMetadata, registerJiraWebhook, deleteJiraWebhook, isJiraEnabled, initJira, handleJiraWebhook, handleAgentSessionEvent, handleIssueUpdate, handleIssueDelete, handleAgentSessionPrompted, isLinearEnabled, initLinear, loadGlobalConfigsIntoEnv, reloadGlobalConfigsAndIntegrations, scheduleIntegrationsReload, handleCore, canClaim, emitBudgetRefusalSideEffects, taskActionInputSchema, taskActionOutputSchema, taskActionHandler, hasCapability, getEnabledCapabilities, createServer, writeSkillsToFilesystem };
|