@agentconnect.md/daemon 1.0.0-rc.100 → 1.0.0-rc.101
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 +77 -65
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -24896,7 +24896,7 @@ const SLACK_TOOLS = [
|
|
|
24896
24896
|
inputSchema: obj({
|
|
24897
24897
|
text: {
|
|
24898
24898
|
type: "string",
|
|
24899
|
-
description: "Message body, in Slack mrkdwn."
|
|
24899
|
+
description: "Message body, in standard Markdown (CommonMark/GFM). Use `**bold**`, `~~strikethrough~~`, `# headings`, tables, etc. — Slack renders standard Markdown natively (do NOT use single-char Slack mrkdwn like `*bold*` or `~strike~`)."
|
|
24900
24900
|
},
|
|
24901
24901
|
channel: {
|
|
24902
24902
|
type: "string",
|
|
@@ -80973,7 +80973,7 @@ var import_dist = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((expor
|
|
|
80973
80973
|
}
|
|
80974
80974
|
});
|
|
80975
80975
|
})))(), 1);
|
|
80976
|
-
const MENTION_RE = /<@([A-Z0-9]+)>/g;
|
|
80976
|
+
const MENTION_RE$1 = /<@([A-Z0-9]+)>/g;
|
|
80977
80977
|
/** Map a Slack file element to a NormalizedMessage Attachment, dropping any
|
|
80978
80978
|
* without a usable id + fetch URL (e.g. external/tombstoned files). Tolerates a
|
|
80979
80979
|
* malformed (null / non-object) element off the wire without throwing. */
|
|
@@ -80991,7 +80991,7 @@ function toAttachment(f) {
|
|
|
80991
80991
|
}
|
|
80992
80992
|
function normalizeSlackEvent(event, ctx) {
|
|
80993
80993
|
const text = event.text ?? "";
|
|
80994
|
-
const mentionedBots = [...text.matchAll(MENTION_RE)].map((m) => m[1]);
|
|
80994
|
+
const mentionedBots = [...text.matchAll(MENTION_RE$1)].map((m) => m[1]);
|
|
80995
80995
|
const attachments = (event.files ?? []).map(toAttachment).filter((a) => a !== null);
|
|
80996
80996
|
return {
|
|
80997
80997
|
msgId: `slack:${event.channel}:${event.ts}`,
|
|
@@ -81098,6 +81098,21 @@ function consolidate(agents) {
|
|
|
81098
81098
|
}
|
|
81099
81099
|
/** Cap on members enriched per `listChannelMembers` call (bounds users.info fan-out). */
|
|
81100
81100
|
const MEMBER_ENRICH_CAP = 50;
|
|
81101
|
+
/**
|
|
81102
|
+
* Build a chat.postMessage/update payload that renders the body as a Block Kit
|
|
81103
|
+
* `markdown` block — Slack renders standard CommonMark there natively, so the agent's
|
|
81104
|
+
* markdown is sent verbatim (no markdown→mrkdwn conversion). `text` is kept as the
|
|
81105
|
+
* notification/accessibility fallback. Callers pre-chunk to SLACK_MARKDOWN_BLOCK_LIMIT.
|
|
81106
|
+
*/
|
|
81107
|
+
function markdownBlock(text) {
|
|
81108
|
+
return {
|
|
81109
|
+
text,
|
|
81110
|
+
blocks: [{
|
|
81111
|
+
type: "markdown",
|
|
81112
|
+
text
|
|
81113
|
+
}]
|
|
81114
|
+
};
|
|
81115
|
+
}
|
|
81101
81116
|
var SlackConnection = class {
|
|
81102
81117
|
deps;
|
|
81103
81118
|
app;
|
|
@@ -81170,8 +81185,8 @@ var SlackConnection = class {
|
|
|
81170
81185
|
return this.queue.enqueue(async () => {
|
|
81171
81186
|
return (await this.app.client.chat.postMessage({
|
|
81172
81187
|
channel,
|
|
81173
|
-
|
|
81174
|
-
|
|
81188
|
+
thread_ts: threadTs,
|
|
81189
|
+
...markdownBlock(text)
|
|
81175
81190
|
}))?.ts;
|
|
81176
81191
|
});
|
|
81177
81192
|
}
|
|
@@ -81183,7 +81198,7 @@ var SlackConnection = class {
|
|
|
81183
81198
|
await this.app.client.chat.update({
|
|
81184
81199
|
channel,
|
|
81185
81200
|
ts,
|
|
81186
|
-
text
|
|
81201
|
+
...markdownBlock(text)
|
|
81187
81202
|
});
|
|
81188
81203
|
} catch (err) {
|
|
81189
81204
|
this.deps.log?.debug(`slack: chat.update failed (ch=${channel} ts=${ts}): ${err.message}`);
|
|
@@ -81356,6 +81371,31 @@ var SlackConnection = class {
|
|
|
81356
81371
|
}
|
|
81357
81372
|
};
|
|
81358
81373
|
//#endregion
|
|
81374
|
+
//#region src/slack/mentions.ts
|
|
81375
|
+
/**
|
|
81376
|
+
* Slack `<@U…>` user-mention helpers, shared by the name-resolver (which warms the
|
|
81377
|
+
* display-name cache for mentioned users) and the session reader (which rewrites
|
|
81378
|
+
* mentions to `@name` for read-back). Slack encodes a user mention as `<@U123>` or
|
|
81379
|
+
* `<@U123|label>`; the id is a `U…`/`W…` user id (resolvable via users.info even for
|
|
81380
|
+
* a bot's user id, unlike a bot SENDER's `B…` id).
|
|
81381
|
+
*/
|
|
81382
|
+
const MENTION_RE = /<@([UW][A-Z0-9]+)(?:\|[^>]*)?>/g;
|
|
81383
|
+
/** Distinct `U…/W…` user ids mentioned in a message body (empty for none/nullish). */
|
|
81384
|
+
function mentionedUserIds(text) {
|
|
81385
|
+
if (!text) return [];
|
|
81386
|
+
const ids = /* @__PURE__ */ new Set();
|
|
81387
|
+
for (const m of text.matchAll(MENTION_RE)) ids.add(m[1]);
|
|
81388
|
+
return [...ids];
|
|
81389
|
+
}
|
|
81390
|
+
/** Rewrite `<@U123>` / `<@U123|x>` to `@<display name>` when the id is known; an
|
|
81391
|
+
* unknown id is left as the raw token so the web renderer's own fallback applies. */
|
|
81392
|
+
function substituteUserMentions(text, names) {
|
|
81393
|
+
return text.replace(MENTION_RE, (raw, id) => {
|
|
81394
|
+
const name = names.get(id);
|
|
81395
|
+
return name ? `@${name}` : raw;
|
|
81396
|
+
});
|
|
81397
|
+
}
|
|
81398
|
+
//#endregion
|
|
81359
81399
|
//#region src/slack/name-resolver.ts
|
|
81360
81400
|
/**
|
|
81361
81401
|
* Resolves Slack ids (channel "C…", user "U…/W…") to human display names off the
|
|
@@ -81385,10 +81425,14 @@ var SlackNameResolver = class {
|
|
|
81385
81425
|
this.log = log;
|
|
81386
81426
|
this.now = now;
|
|
81387
81427
|
}
|
|
81388
|
-
/** Kick off (cached) resolution for a message's channel
|
|
81428
|
+
/** Kick off (cached) resolution for a message's channel, human sender, and any
|
|
81429
|
+
* `<@U…>` user mentions in its body — so session read-back can render mentions
|
|
81430
|
+
* as `@name` (not the raw id). Mention ids use the `U…/W…` user id (resolvable
|
|
81431
|
+
* via users.info even for a bot's user id), unlike a bot SENDER's `B…` id. */
|
|
81389
81432
|
noteMessage(conn, msg) {
|
|
81390
81433
|
this.resolveChannel(conn, msg.channel);
|
|
81391
81434
|
if (!msg.sender.isBot && /^[UW]/.test(msg.sender.id)) this.resolveUser(conn, msg.sender.id);
|
|
81435
|
+
for (const id of mentionedUserIds(msg.text)) this.resolveUser(conn, id);
|
|
81392
81436
|
}
|
|
81393
81437
|
claim(id) {
|
|
81394
81438
|
const at = this.nextAttemptAt.get(id);
|
|
@@ -81426,61 +81470,22 @@ var SlackNameResolver = class {
|
|
|
81426
81470
|
//#endregion
|
|
81427
81471
|
//#region src/slack/formatter.ts
|
|
81428
81472
|
/**
|
|
81429
|
-
* Last-mile Slack formatting (§9.1 / §9.3).
|
|
81430
|
-
* -
|
|
81431
|
-
* - splitIntoSections: break a message into <= maxLen sections (Slack ~3000/section).
|
|
81432
|
-
* Both are platform-specific; the upstream convergence (ACP->IR) stays platform-agnostic.
|
|
81433
|
-
*/
|
|
81434
|
-
/** Slack section soft cap (§9.3: Slack splits at ~3000 chars per section). */
|
|
81435
|
-
const SLACK_SECTION_LIMIT = 3e3;
|
|
81436
|
-
const CODE_SENTINEL = "\0";
|
|
81437
|
-
const BOLD = "";
|
|
81438
|
-
/** Replace fenced ```code``` and inline `code` with placeholders so inline
|
|
81439
|
-
* formatting rules never fire inside them; returns the masked text + restorer. */
|
|
81440
|
-
function protectCode(input) {
|
|
81441
|
-
const spans = [];
|
|
81442
|
-
const masked = input.replace(/```[\s\S]*?```|`[^`\n]*`/g, (m) => {
|
|
81443
|
-
return `${CODE_SENTINEL}${spans.push(m) - 1}${CODE_SENTINEL}`;
|
|
81444
|
-
});
|
|
81445
|
-
const restore = (s) => s.replace(new RegExp(`${CODE_SENTINEL}(\\d+)${CODE_SENTINEL}`, "g"), (_, i) => spans[Number(i)] ?? "");
|
|
81446
|
-
return {
|
|
81447
|
-
masked,
|
|
81448
|
-
restore
|
|
81449
|
-
};
|
|
81450
|
-
}
|
|
81451
|
-
/**
|
|
81452
|
-
* Convert common Markdown to Slack mrkdwn. Handled: bold (`**`/`__` -> `*`),
|
|
81453
|
-
* italic (`*` -> `_`; bare `_` is already valid mrkdwn italic and left alone to
|
|
81454
|
-
* avoid mangling snake_case), strikethrough (`~~` -> `~`), links (`[t](u)` ->
|
|
81455
|
-
* `<u|t>`), ATX headings (`# H` -> `*H*`), and `-`/`*`/`+` bullets -> `•`. Code
|
|
81456
|
-
* spans and fences are preserved verbatim.
|
|
81473
|
+
* Last-mile Slack formatting (§9.1 / §9.3). One pure helper:
|
|
81474
|
+
* - splitIntoSections: break a message into <= maxLen sections.
|
|
81457
81475
|
*
|
|
81458
|
-
*
|
|
81459
|
-
*
|
|
81460
|
-
*
|
|
81476
|
+
* The daemon posts the agent's reply as a Block Kit `markdown` block (which renders
|
|
81477
|
+
* standard CommonMark natively), so NO markdown→mrkdwn conversion happens here — the
|
|
81478
|
+
* agent's text is sent verbatim. We only chunk it: a single `markdown` block caps at
|
|
81479
|
+
* 12000 chars, so a long body is split across blocks/messages on line boundaries.
|
|
81461
81480
|
*/
|
|
81462
|
-
|
|
81463
|
-
|
|
81464
|
-
const { masked, restore } = protectCode(input);
|
|
81465
|
-
let out = masked;
|
|
81466
|
-
out = out.replace(/^[ \t]*#{1,6}[ \t]+(.+?)[ \t]*#*$/gm, (_m, h) => `${BOLD}${h.replace(/\*+|__/g, "")}${BOLD}`);
|
|
81467
|
-
out = out.replace(/^([ \t]*)[-*+][ \t]+/gm, "$1• ");
|
|
81468
|
-
out = out.replace(/\[([^\]]+)\]\((https?:\/\/[^)\s]+)\)/g, "<$2|$1>");
|
|
81469
|
-
out = out.replace(/\*\*\*(?!\s)([^\n]+?)(?<!\s)\*\*\*/g, `${BOLD}_$1_${BOLD}`);
|
|
81470
|
-
out = out.replace(/___(?!\s)([^\n]+?)(?<!\s)___/g, `${BOLD}_$1_${BOLD}`);
|
|
81471
|
-
out = out.replace(/\*\*(?!\s)([^\n]+?)(?<!\s)\*\*/g, `${BOLD}$1${BOLD}`);
|
|
81472
|
-
out = out.replace(/__(?!\s)([^\n]+?)(?<!\s)__/g, `${BOLD}$1${BOLD}`);
|
|
81473
|
-
out = out.replace(/\*(?!\s)([^*\n]+?)(?<!\s)\*/g, "_$1_");
|
|
81474
|
-
out = out.split(BOLD).join("*");
|
|
81475
|
-
out = out.replace(/~~([^\n]+?)~~/g, "~$1~");
|
|
81476
|
-
return restore(out);
|
|
81477
|
-
}
|
|
81481
|
+
/** Slack `markdown` block hard cap (12000 chars per block). */
|
|
81482
|
+
const SLACK_MARKDOWN_BLOCK_LIMIT = 12e3;
|
|
81478
81483
|
/**
|
|
81479
81484
|
* Split text into sections no longer than maxLen, preferring line boundaries;
|
|
81480
81485
|
* a single line longer than maxLen is hard-cut. Whitespace-only input yields no
|
|
81481
81486
|
* sections. Content is never lost across sections.
|
|
81482
81487
|
*/
|
|
81483
|
-
function splitIntoSections(text, maxLen =
|
|
81488
|
+
function splitIntoSections(text, maxLen = SLACK_MARKDOWN_BLOCK_LIMIT) {
|
|
81484
81489
|
if (!text.trim()) return [];
|
|
81485
81490
|
if (text.length <= maxLen) return [text];
|
|
81486
81491
|
const sections = [];
|
|
@@ -81520,9 +81525,10 @@ function planIcon(status) {
|
|
|
81520
81525
|
if (status === "in_progress") return ":hourglass_flowing_sand:";
|
|
81521
81526
|
return ":white_large_square:";
|
|
81522
81527
|
}
|
|
81523
|
-
/** Render an ACP plan (full entry list, resent on every update) as a compact summary.
|
|
81528
|
+
/** Render an ACP plan (full entry list, resent on every update) as a compact summary.
|
|
81529
|
+
* Posted as a `markdown` block, so emphasis is CommonMark (`**bold**`), not mrkdwn. */
|
|
81524
81530
|
function renderPlan(entries) {
|
|
81525
|
-
return [":clipboard:
|
|
81531
|
+
return [":clipboard: **Plan**", ...entries.map((e) => `${planIcon(e.status)} ${clampLabel(e.content ?? "")}`)].join("\n");
|
|
81526
81532
|
}
|
|
81527
81533
|
var OutputConverger = class {
|
|
81528
81534
|
mode;
|
|
@@ -81537,8 +81543,9 @@ var OutputConverger = class {
|
|
|
81537
81543
|
hasBuffered() {
|
|
81538
81544
|
return this.buf.trim().length > 0;
|
|
81539
81545
|
}
|
|
81540
|
-
/** Flush the buffered body:
|
|
81541
|
-
* one `post` per section (
|
|
81546
|
+
/** Flush the buffered body: split the agent's verbatim markdown into ≤block-limit
|
|
81547
|
+
* sections, one `post` per section (each posted as a Block Kit `markdown` block, so
|
|
81548
|
+
* no mrkdwn conversion). Public so the daemon's idle timer can call it. */
|
|
81542
81549
|
flushBuffered() {
|
|
81543
81550
|
return this.flush();
|
|
81544
81551
|
}
|
|
@@ -81546,7 +81553,7 @@ var OutputConverger = class {
|
|
|
81546
81553
|
const text = this.buf;
|
|
81547
81554
|
this.buf = "";
|
|
81548
81555
|
if (!text.trim()) return [];
|
|
81549
|
-
return splitIntoSections(
|
|
81556
|
+
return splitIntoSections(text).map((t) => ({
|
|
81550
81557
|
kind: "post",
|
|
81551
81558
|
text: t
|
|
81552
81559
|
}));
|
|
@@ -81637,7 +81644,7 @@ var OutputConverger = class {
|
|
|
81637
81644
|
if (this.mode === "low") return [...this.flush(), clear];
|
|
81638
81645
|
const footer = link ? [{
|
|
81639
81646
|
kind: "notice",
|
|
81640
|
-
text: `:white_check_mark: done —
|
|
81647
|
+
text: `:white_check_mark: done — [details](${link})`
|
|
81641
81648
|
}] : [];
|
|
81642
81649
|
return [
|
|
81643
81650
|
...this.flush(),
|
|
@@ -83293,11 +83300,16 @@ function createSessionReader(store) {
|
|
|
83293
83300
|
return {
|
|
83294
83301
|
list(req) {
|
|
83295
83302
|
const rows = store.listSessions(req.agentId);
|
|
83296
|
-
const names = store.getDisplayNames(rows.flatMap((r) => [
|
|
83303
|
+
const names = store.getDisplayNames(rows.flatMap((r) => [
|
|
83304
|
+
r.channel,
|
|
83305
|
+
...r.triggeredBy ? [r.triggeredBy] : [],
|
|
83306
|
+
...mentionedUserIds(r.title)
|
|
83307
|
+
]));
|
|
83297
83308
|
return { sessions: rows.map((r) => {
|
|
83298
83309
|
const usage = parseUsage(r.usage);
|
|
83299
83310
|
const channelName = names.get(r.channel);
|
|
83300
83311
|
const triggeredByName = r.triggeredBy ? names.get(r.triggeredBy) : void 0;
|
|
83312
|
+
const title = r.title ? substituteUserMentions(r.title, names) : void 0;
|
|
83301
83313
|
return {
|
|
83302
83314
|
sessionId: r.acpSessionId,
|
|
83303
83315
|
sessionKey: {
|
|
@@ -83309,7 +83321,7 @@ function createSessionReader(store) {
|
|
|
83309
83321
|
status: r.state,
|
|
83310
83322
|
lastActivityAt: new Date(r.updatedAt).toISOString(),
|
|
83311
83323
|
...usage ? { usage } : {},
|
|
83312
|
-
...
|
|
83324
|
+
...title ? { title } : {},
|
|
83313
83325
|
...r.triggeredBy ? { triggeredBy: r.triggeredBy } : {},
|
|
83314
83326
|
...channelName ? { channelName } : {},
|
|
83315
83327
|
...triggeredByName ? { triggeredByName } : {}
|
|
@@ -83325,7 +83337,7 @@ function createSessionReader(store) {
|
|
|
83325
83337
|
const before = req.cursor != null && Number.isFinite(Number(req.cursor)) ? Number(req.cursor) : null;
|
|
83326
83338
|
const { rows, hasMore } = store.transcriptPage(rec.channel, rec.thread, before, req.limit);
|
|
83327
83339
|
const ordered = rows.slice().reverse();
|
|
83328
|
-
const names = store.getDisplayNames(ordered.
|
|
83340
|
+
const names = store.getDisplayNames(ordered.flatMap((r) => [r.sender, ...mentionedUserIds(r.text)]));
|
|
83329
83341
|
const built = ordered.map((r) => {
|
|
83330
83342
|
const senderName = names.get(r.sender);
|
|
83331
83343
|
const base = {
|
|
@@ -83334,7 +83346,7 @@ function createSessionReader(store) {
|
|
|
83334
83346
|
...senderName ? { senderName } : {},
|
|
83335
83347
|
ts: r.ts,
|
|
83336
83348
|
kind: r.kind,
|
|
83337
|
-
text: r.text
|
|
83349
|
+
text: substituteUserMentions(r.text, names)
|
|
83338
83350
|
};
|
|
83339
83351
|
if (r.kind !== "tool" || !r.body) return base;
|
|
83340
83352
|
let full;
|