@agentconnect.md/daemon 1.0.0-rc.344 → 1.0.0-rc.346

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.
@@ -3,10 +3,10 @@ import { n as require_ms, t as require_src$3 } from "./src-DDtlVutj.js";
3
3
  import { n as init_esm_shims, t as __dirname } from "./esm-shims-DxGcm4Hw.js";
4
4
  import { t as DAEMON_VERSION } from "./version-DGaRXjBK.js";
5
5
  import { C as _coercedNumber, T as NEVER, a as array, d as literal, f as looseObject, i as any, m as object, o as boolean, p as number$1, t as ZodNumber, v as string, x as url$4 } from "./schemas-CQkO9hC5.js";
6
- import { _ as RESERVED_MCP_SERVER_NAME, a as RELAY_DAEMON_WS_PATH, c as SHARED_CONFIG_ACTION_ID, d as encode, f as MAX_FRAME_BYTES, h as HookReport, i as RELAY_DAEMON_SUBPROTOCOL, l as buildEnvelope, n as gitRepoLabel, o as buildRelayDaemonFrame, r as normalizeGitUrl, s as decodeRelayDaemonFrame, u as decodeEnvelope, v as ZodIssueCode } from "./dist-MrCr7fl1.js";
7
- import { a as agentChildEnv, c as persistRelays, i as selectAgent, n as findAgentFiles, o as loadConfig, r as loadAgents, s as persistDaemonId } from "./load-agents-BAQcjcVh.js";
6
+ import { _ as HookReport, a as RELAY_DAEMON_WS_PATH, b as ZodIssueCode, c as SHARED_AGENT_SELECT_ACTION_ID, d as buildEnvelope, f as decodeEnvelope, i as RELAY_DAEMON_SUBPROTOCOL, l as SLACK_STATUS_ACTION, m as MAX_FRAME_BYTES, n as gitRepoLabel, o as buildRelayDaemonFrame, p as encode, r as normalizeGitUrl, s as decodeRelayDaemonFrame, u as encodeSharedSlackStatusTarget, y as RESERVED_MCP_SERVER_NAME } from "./dist-nbZ5Ymz0.js";
7
+ import { a as agentChildEnv, c as persistRelays, i as selectAgent, n as findAgentFiles, o as loadConfig, r as loadAgents, s as persistDaemonId } from "./load-agents-D02wd-zM.js";
8
8
  import { l as resolveRoot, o as mcpSocketPath, u as statePath } from "./paths-DEcUO8lC.js";
9
- import { C as toolsForIntegrations, E as resolveRuntimes, S as MEMORY_TOOL_NAMES, T as cachedRuntimeVersions, _ as listMemory, a as prefetchWorkspace, b as ALL_TOOL_NAMES, c as probeGitVersion, d as createMemoryProvider, f as memoryKindOf, g as MemoryTooLargeError, h as MemoryPathError, i as installedRuntimes, l as sessionGitEnv, m as MemoryConflictError, n as turnFailureCode, o as prepareWorkspace, p as memoryProviderFor, r as turnFailureReason, s as initGitInjection, t as AcpHost, u as simpleGit, v as resolveInMemoryDir, w as cachedRuntimeNames, x as GITHUB_REVIEW_TOOLS, y as writeMemoryFile } from "./acp-host-rssqAAfr.js";
9
+ import { C as toolsForIntegrations, E as resolveRuntimes, S as MEMORY_TOOL_NAMES, T as cachedRuntimeVersions, _ as listMemory, a as prefetchWorkspace, b as ALL_TOOL_NAMES, c as probeGitVersion, d as createMemoryProvider, f as memoryKindOf, g as MemoryTooLargeError, h as MemoryPathError, i as installedRuntimes, l as sessionGitEnv, m as MemoryConflictError, n as turnFailureCode, o as prepareWorkspace, p as memoryProviderFor, r as turnFailureReason, s as initGitInjection, t as AcpHost, u as simpleGit, v as resolveInMemoryDir, w as cachedRuntimeNames, x as GITHUB_REVIEW_TOOLS, y as writeMemoryFile } from "./acp-host-cFIM27G4.js";
10
10
  import { i as GitCredentialCache, n as gitcredSocketPath, r as writeGitcredShim, t as GitCredServer } from "./gitcred-server-Bu-Svl7L.js";
11
11
  import { B as ToolListChangedNotificationSchema, D as ListPromptsResultSchema, E as ListChangedOptionsBaseSchema, F as PromptListChangedNotificationSchema, G as isZ4Schema, H as isJSONRPCRequest, I as ReadResourceResultSchema, J as encodeFrame, K as safeParse, L as ResourceListChangedNotificationSchema, M as ListToolsResultSchema, O as ListResourceTemplatesResultSchema, P as McpError, R as SUPPORTED_PROTOCOL_VERSIONS, S as InitializeResultSchema, T as LATEST_PROTOCOL_VERSION, U as isJSONRPCResultResponse, V as isInitializedNotification, W as getObjectShape, _ as ElicitResultSchema, a as AjvJsonSchemaValidator, b as GetPromptResultSchema, c as mergeCapabilities, d as CompleteResultSchema, f as CreateMessageRequestSchema, g as ElicitRequestSchema, h as CreateTaskResultSchema, i as assertToolsCallTaskCapability, k as ListResourcesResultSchema, m as CreateMessageResultWithToolsSchema, n as serializeMessage, o as require_fast_deep_equal, p as CreateMessageResultSchema, q as decodeFrames, r as assertClientRequestTaskCapability, s as Protocol, t as ReadBuffer, u as CallToolResultSchema, v as EmptyResultSchema, w as JSONRPCMessageSchema, y as ErrorCode } from "./stdio-DnS8WcXi.js";
12
12
  import { createRequire } from "node:module";
@@ -68195,8 +68195,10 @@ function attributionMrkdwn(info) {
68195
68195
  const model = escapeMrkdwnLabel(info.model);
68196
68196
  return `sent by <${info.botUrl}|${botName}> (${runtime} · ${model}) · <${info.sessionUrl}|open in session>`;
68197
68197
  }
68198
- /** The classic per-turn footer is a compact secondary/gray Slack `context` block. */
68199
- function attributionBlocks(info) {
68198
+ /** The per-turn footer is a compact secondary/gray Slack `context` block. Exported so
68199
+ * the daemon can include it in the reply's initial chat.postMessage (the only reliable
68200
+ * point at which Slack's unfurl controls apply). */
68201
+ function buildAttributionBlocks(info) {
68200
68202
  return {
68201
68203
  text: attributionText(info),
68202
68204
  blocks: [{
@@ -68208,29 +68210,6 @@ function attributionBlocks(info) {
68208
68210
  }]
68209
68211
  };
68210
68212
  }
68211
- /** The shared-bot variant uses the same text but a `section`, because only sections take
68212
- * the config button accessory. The click lands on the relay (it owns Socket Mode). */
68213
- function sharedFooterBlocks(info) {
68214
- return {
68215
- text: attributionText(info),
68216
- blocks: [{
68217
- type: "section",
68218
- text: {
68219
- type: "mrkdwn",
68220
- text: attributionMrkdwn(info)
68221
- },
68222
- accessory: {
68223
- type: "button",
68224
- text: {
68225
- type: "plain_text",
68226
- text: "👤",
68227
- emoji: true
68228
- },
68229
- action_id: SHARED_CONFIG_ACTION_ID
68230
- }
68231
- }]
68232
- };
68233
- }
68234
68213
  /** Pull human-readable output text out of an ACP tool_call/_update. Prefers the `content[]`
68235
68214
  * text blocks (the tool's reported output); falls back to a string `rawOutput`. diff /
68236
68215
  * terminal blocks and non-string rawOutput are skipped — they have no compact inline text. */
@@ -68288,18 +68267,9 @@ function renderStatusBar(info) {
68288
68267
  if (info.totalTokens !== void 0) parts.push(`${compactCount$2(info.totalTokens)} tok`);
68289
68268
  return `:bar_chart: ${parts.length ? parts.join(" · ") : "—"}`;
68290
68269
  }
68291
- /** Slack action_ids for the interactive status bar + modal (matched by the connection's
68292
- * handlers). Kept here so the builders and the handlers can't drift apart. */
68293
- const STATUS_ACTION = {
68294
- manage: "ac_manage",
68295
- setModel: "ac_set_model",
68296
- setEffort: "ac_set_effort",
68297
- setPermissionMode: "ac_set_permission_mode",
68298
- setFast: "ac_set_fast",
68299
- setOutput: "ac_set_output",
68300
- cancel: "ac_cancel",
68301
- view: "ac_view"
68302
- };
68270
+ /** Slack action_ids for the interactive status bar + modal. The protocol owns the
68271
+ * values because a shared bot receives the same actions on the relay first. */
68272
+ const STATUS_ACTION = SLACK_STATUS_ACTION;
68303
68273
  /** The modal view's callback_id — used if we ever handle a submit (controls apply on
68304
68274
  * interaction today, so there's no submit). */
68305
68275
  const STATUS_MODAL_CALLBACK = "ac_status_modal";
@@ -68486,16 +68456,63 @@ function buildElicitationResolvedCard(params, decision) {
68486
68456
  }
68487
68457
  }];
68488
68458
  }
68489
- /** Build the compact in-thread status message as one Slack row: status text with an
68490
- * inline View Session URL, plus one Configure accessory button. Slack sections allow
68491
- * only one accessory, and mrkdwn links cannot dispatch block_actions, so Cancel stays in
68492
- * the Configure modal rather than becoming a non-functional inline link. */
68493
- function buildStatusBlocks(info, sessionKey, link) {
68459
+ /** Build the compact in-thread status message. Dedicated bots retain the single
68460
+ * Configure accessory. A shared bot shows its current agent in an inline external
68461
+ * select (the relay supplies options), followed by the session's two immediate
68462
+ * actions: Options opens the existing controls modal and Cancel stops this turn. */
68463
+ function buildStatusBlocks(info, sessionKey, link, shared) {
68464
+ const text = link ? `${renderStatusBar(info)} · <${link}|View Session>` : renderStatusBar(info);
68465
+ if (shared) {
68466
+ const currentAgent = {
68467
+ text: {
68468
+ type: "plain_text",
68469
+ text: clampTo$2(shared.currentAgent.name, 75) || shared.currentAgent.id
68470
+ },
68471
+ value: shared.currentAgent.id
68472
+ };
68473
+ return [{
68474
+ type: "section",
68475
+ text: {
68476
+ type: "mrkdwn",
68477
+ text
68478
+ },
68479
+ accessory: {
68480
+ type: "external_select",
68481
+ action_id: SHARED_AGENT_SELECT_ACTION_ID,
68482
+ placeholder: {
68483
+ type: "plain_text",
68484
+ text: "Select agent"
68485
+ },
68486
+ initial_option: currentAgent,
68487
+ min_query_length: 0
68488
+ }
68489
+ }, {
68490
+ type: "actions",
68491
+ elements: [{
68492
+ type: "button",
68493
+ action_id: STATUS_ACTION.manage,
68494
+ text: {
68495
+ type: "plain_text",
68496
+ text: "Options"
68497
+ },
68498
+ value: shared.sessionTarget
68499
+ }, {
68500
+ type: "button",
68501
+ action_id: STATUS_ACTION.cancel,
68502
+ text: {
68503
+ type: "plain_text",
68504
+ text: "Cancel"
68505
+ },
68506
+ style: "danger",
68507
+ value: shared.sessionTarget
68508
+ }]
68509
+ }];
68510
+ }
68494
68511
  return [{
68495
68512
  type: "section",
68496
68513
  text: {
68497
68514
  type: "mrkdwn",
68498
- text: link ? `${renderStatusBar(info)} · <${link}|View Session>` : renderStatusBar(info)
68515
+ text
68499
68516
  },
68500
68517
  accessory: {
68501
68518
  type: "button",
@@ -68514,13 +68531,13 @@ function fmtCount(n) {
68514
68531
  }
68515
68532
  /**
68516
68533
  * Build the controls modal opened from Configure (a snapshot — Slack modals don't
68517
- * stream). `private_metadata` carries the session key so the modal's `block_actions`
68518
- * (model/effort/fast/output selects, Cancel) resolve the session. Blocks: the model /
68534
+ * stream). `private_metadata` carries either the direct session key or a shared-bot
68535
+ * routing target so the modal's `block_actions` resolve the session. Blocks: the model /
68519
68536
  * effort / fast / output selectors (each omitted when not applicable), a labeled usage
68520
68537
  * grid (context · tokens · cost · per-type breakdown), and Cancel + View-session (URL,
68521
68538
  * when `link`). The modal has room, so each metric gets its own labeled cell. Pure.
68522
68539
  */
68523
- function buildStatusModal(info, sessionKey, link) {
68540
+ function buildStatusModal(info, sessionKey, link, privateMetadata = sessionKey) {
68524
68541
  const blocks = [];
68525
68542
  const models = info.models ?? [];
68526
68543
  if (models.length > 0) {
@@ -68720,7 +68737,7 @@ function buildStatusModal(info, sessionKey, link) {
68720
68737
  return {
68721
68738
  type: "modal",
68722
68739
  callback_id: STATUS_MODAL_CALLBACK,
68723
- private_metadata: sessionKey,
68740
+ private_metadata: privateMetadata,
68724
68741
  title: {
68725
68742
  type: "plain_text",
68726
68743
  text: "Session"
@@ -68881,7 +68898,7 @@ var OutputConverger = class {
68881
68898
  };
68882
68899
  const attribution = info ? [{
68883
68900
  kind: "attribution",
68884
- ...info.shared ? sharedFooterBlocks(info) : attributionBlocks(info)
68901
+ ...buildAttributionBlocks(info)
68885
68902
  }] : [];
68886
68903
  if (this.mode === "low") return [
68887
68904
  ...this.flush(),
@@ -69092,16 +69109,7 @@ var SlackConnection = class {
69092
69109
  const sessionKey = action.value;
69093
69110
  const triggerId = body?.trigger_id;
69094
69111
  if (!sessionKey || !triggerId) return;
69095
- const data = this.deps.onStatusInfo?.(sessionKey);
69096
- if (!data) return;
69097
- try {
69098
- await this.app.client.views.open({
69099
- trigger_id: triggerId,
69100
- view: buildStatusModal(data.info, sessionKey, data.link)
69101
- });
69102
- } catch (err) {
69103
- this.deps.log?.debug(`slack: views.open failed: ${err.message}`);
69104
- }
69112
+ await this.openStatusModal(triggerId, sessionKey);
69105
69113
  });
69106
69114
  this.app.action(STATUS_ACTION.setModel, async ({ ack, action, body }) => {
69107
69115
  await ack();
@@ -69188,6 +69196,22 @@ var SlackConnection = class {
69188
69196
  await this.app.start();
69189
69197
  log?.debug("slack: app.start resolved → socket established");
69190
69198
  }
69199
+ /** Open the existing per-session controls modal. Direct bots call this from their
69200
+ * local Socket Mode action handler; shared bots call it after the relay forwards the
69201
+ * click over `rd/msg(slack_action)`. `privateMetadata` stays opaque to Slack and lets the
69202
+ * relay route subsequent select/cancel interactions back to this daemon. */
69203
+ async openStatusModal(triggerId, sessionKey, privateMetadata = sessionKey) {
69204
+ const data = this.deps.onStatusInfo?.(sessionKey);
69205
+ if (!data) return;
69206
+ try {
69207
+ await this.app.client.views.open({
69208
+ trigger_id: triggerId,
69209
+ view: buildStatusModal(data.info, sessionKey, data.link, privateMetadata)
69210
+ });
69211
+ } catch (err) {
69212
+ this.deps.log?.debug(`slack: views.open failed: ${err.message}`);
69213
+ }
69214
+ }
69191
69215
  rememberAssistantThread(ev) {
69192
69216
  const channel = ev.assistant_thread?.channel_id;
69193
69217
  const threadTs = ev.assistant_thread?.thread_ts;
@@ -69235,10 +69259,17 @@ var SlackConnection = class {
69235
69259
  }
69236
69260
  async postMessage(channel, text, threadTs, options) {
69237
69261
  return this.queue.enqueue(async () => {
69262
+ const body = markdownBlock(text);
69263
+ const trailing = options?.trailingBlocks;
69238
69264
  return (await this.postChatMessage({
69239
69265
  channel,
69240
69266
  thread_ts: threadTs,
69241
- ...markdownBlock(text)
69267
+ ...body,
69268
+ ...trailing?.length ? {
69269
+ blocks: [...body.blocks, ...trailing],
69270
+ unfurl_links: false,
69271
+ unfurl_media: false
69272
+ } : {}
69242
69273
  }, options))?.ts;
69243
69274
  });
69244
69275
  }
@@ -69273,13 +69304,12 @@ var SlackConnection = class {
69273
69304
  }, options))?.ts;
69274
69305
  });
69275
69306
  }
69276
- /** Edit a previously-posted Block Kit message in place (chat.update). Best-effort.
69277
- * `unfurl_links: false` keeps an edit from unfurling console URLs. `text` is optional:
69278
- * footer attachment omits it so Slack retains the original notification fallback and
69279
- * a 12k markdown block cannot overflow chat.update's 4k top-level text limit. */
69307
+ /** Edit a previously-posted Block Kit message in place (chat.update). Best-effort;
69308
+ * returns false after logging so footer migration can retry a failed cleanup. `text`
69309
+ * is optional so callers can preserve the original notification fallback. */
69280
69310
  async updateBlocks(channel, ts, blocks, text) {
69281
- await this.queue.enqueue(async () => {
69282
- try {
69311
+ try {
69312
+ return await this.queue.enqueue(async () => {
69283
69313
  await this.app.client.chat.update({
69284
69314
  channel,
69285
69315
  ts,
@@ -69288,10 +69318,12 @@ var SlackConnection = class {
69288
69318
  unfurl_links: false,
69289
69319
  unfurl_media: false
69290
69320
  });
69291
- } catch (err) {
69292
- this.deps.log?.debug(`slack: chat.update (blocks) failed (ch=${channel} ts=${ts}): ${err.message}`);
69293
- }
69294
- });
69321
+ return true;
69322
+ });
69323
+ } catch (err) {
69324
+ this.deps.log?.debug(`slack: chat.update (blocks) failed (ch=${channel} ts=${ts}): ${err.message}`);
69325
+ return false;
69326
+ }
69295
69327
  }
69296
69328
  /**
69297
69329
  * Pull a Slack thread's full history (conversations.replies, cursor-paginated)
@@ -217352,11 +217384,11 @@ var Daemon = class Daemon {
217352
217384
  agentCallDeliveries = /* @__PURE__ */ new Map();
217353
217385
  orchestrationDeadlines = /* @__PURE__ */ new Map();
217354
217386
  /**
217355
- * Dispatch one inbound relay item (`rd/msg` — a webchat op or a hook fire) and
217387
+ * Dispatch one inbound relay item (`rd/msg` — webchat, shared IM/action, or hook) and
217356
217388
  * return the `rd/ack` verdict — the relay-path entry, reusing the SAME turn
217357
217389
  * engine + by-key cores as the (retiring) CP path. The `chat` callback streams
217358
217390
  * a webchat reply back as `rd/chat` over the relay socket the op arrived on
217359
- * (no-op for hooks — their outcome goes to the CP as `hook/report`).
217391
+ * (no-op outside webchat; hook outcomes go to the CP as `hook/report`).
217360
217392
  *
217361
217393
  * Deduped by the relay-minted (sessionKey, msgId): the wire is at-least-once, so a
217362
217394
  * retransmitted rd/msg (identical msgId) must NOT run the user's turn a second time —
@@ -217389,7 +217421,7 @@ var Daemon = class Daemon {
217389
217421
  this.pendingRelayMsgAcks.set(dedupKey, task);
217390
217422
  return task;
217391
217423
  }
217392
- const ack = msg.source === "webchat" ? this.dispatchRelayOp(msg, chat) : this.handleRelayIm(msg);
217424
+ const ack = msg.source === "webchat" ? this.dispatchRelayOp(msg, chat) : msg.source === "slack_action" ? this.handleRelaySlackAction(msg) : this.handleRelayIm(msg);
217393
217425
  if (this.relayMsgAcks.size >= 2e3) this.relayMsgAcks.clear();
217394
217426
  this.relayMsgAcks.set(dedupKey, ack);
217395
217427
  return ack;
@@ -217442,6 +217474,97 @@ var Daemon = class Daemon {
217442
217474
  accepted: true
217443
217475
  };
217444
217476
  }
217477
+ /** Apply one shared-bot status interaction after the relay has validated the opaque
217478
+ * target against its live bot assignment. Re-check every daemon-owned boundary before
217479
+ * opening or mutating anything: the agent, shared Slack integration, local connection,
217480
+ * session owner, and (when retained) exact delivery binding must all still agree. */
217481
+ handleRelaySlackAction(msg) {
217482
+ const agent = this.agents.get(msg.agentId);
217483
+ if (!agent) {
217484
+ this.log.warn(`relay: Slack action for unknown agent ${msg.agentId} — dropping`);
217485
+ return {
217486
+ msgId: msg.msgId,
217487
+ accepted: false,
217488
+ reason: "no_agent"
217489
+ };
217490
+ }
217491
+ if (!agent.integrations.find((candidate) => candidate.id === msg.integrationId && candidate.platform === "slack" && candidate.slack.mode === "shared")) {
217492
+ this.log.warn(`relay: Slack action for non-shared integration ${msg.integrationId} on agent ${msg.agentId} — dropping`);
217493
+ return {
217494
+ msgId: msg.msgId,
217495
+ accepted: false,
217496
+ reason: "not_found"
217497
+ };
217498
+ }
217499
+ const conn = this.connByIntegration.get(msg.integrationId);
217500
+ if (!conn) {
217501
+ this.log.warn(`relay: Slack action for unavailable integration ${msg.integrationId} — dropping`);
217502
+ return {
217503
+ msgId: msg.msgId,
217504
+ accepted: false,
217505
+ reason: "unavailable"
217506
+ };
217507
+ }
217508
+ const rec = this.store.getSession(msg.sessionKey);
217509
+ if (!rec || rec.agentId !== msg.agentId || rec.platform !== "slack") {
217510
+ this.log.warn(`relay: Slack action for stale or foreign session ${msg.sessionKey} — dropping`);
217511
+ return {
217512
+ msgId: msg.msgId,
217513
+ accepted: false,
217514
+ reason: "not_found"
217515
+ };
217516
+ }
217517
+ const binding = this.sessionDeliveryBindings.get(msg.sessionKey);
217518
+ if (binding && (binding.agentId !== msg.agentId || binding.platform !== "slack" || binding.integrationId !== msg.integrationId)) {
217519
+ this.log.warn(`relay: Slack action delivery binding mismatch for session ${msg.sessionKey} — dropping`);
217520
+ return {
217521
+ msgId: msg.msgId,
217522
+ accepted: false,
217523
+ reason: "stale"
217524
+ };
217525
+ }
217526
+ const payload = msg.payload;
217527
+ if (payload.kind === "open-config") {
217528
+ const privateMetadata = encodeSharedSlackStatusTarget({
217529
+ agentId: msg.agentId,
217530
+ integrationId: msg.integrationId,
217531
+ sessionKey: msg.sessionKey
217532
+ });
217533
+ conn.openStatusModal(payload.triggerId, msg.sessionKey, privateMetadata);
217534
+ } else if (payload.kind === "set-model") this.handleStatusAction({
217535
+ kind: payload.kind,
217536
+ sessionKey: msg.sessionKey,
217537
+ model: payload.model
217538
+ });
217539
+ else if (payload.kind === "set-effort") this.handleStatusAction({
217540
+ kind: payload.kind,
217541
+ sessionKey: msg.sessionKey,
217542
+ effort: payload.effort
217543
+ });
217544
+ else if (payload.kind === "set-permission-mode") this.handleStatusAction({
217545
+ kind: payload.kind,
217546
+ sessionKey: msg.sessionKey,
217547
+ permissionMode: payload.permissionMode
217548
+ });
217549
+ else if (payload.kind === "set-fast") this.handleStatusAction({
217550
+ kind: payload.kind,
217551
+ sessionKey: msg.sessionKey,
217552
+ fastMode: payload.fastMode
217553
+ });
217554
+ else if (payload.kind === "set-output") this.handleStatusAction({
217555
+ kind: payload.kind,
217556
+ sessionKey: msg.sessionKey,
217557
+ outputMode: payload.outputMode
217558
+ });
217559
+ else this.handleStatusAction({
217560
+ kind: "cancel",
217561
+ sessionKey: msg.sessionKey
217562
+ });
217563
+ return {
217564
+ msgId: msg.msgId,
217565
+ accepted: true
217566
+ };
217567
+ }
217445
217568
  relayAgentMsgAcks = /* @__PURE__ */ new Map();
217446
217569
  /**
217447
217570
  * TARGET side of a cross-daemon `messageAgent` (agent-collaboration §2.3/§6.2, P2) —
@@ -219517,6 +219640,7 @@ var Daemon = class Daemon {
219517
219640
  rec,
219518
219641
  agentId,
219519
219642
  agentName,
219643
+ ...integrationId !== void 0 ? { integrationId } : {},
219520
219644
  ...iconUrl ? { iconUrl } : {},
219521
219645
  platform: msg.platform,
219522
219646
  isDm: msg.isDm,
@@ -219572,6 +219696,20 @@ var Daemon = class Daemon {
219572
219696
  const modelOptions = host.modelOptions?.(sessionId) ?? null;
219573
219697
  const advertisedModel = modelOptions?.current;
219574
219698
  const turnModel = modelOptions === null ? override ?? agent.runtimeOverrides?.model : advertisedModel === "default" ? void 0 : advertisedModel;
219699
+ const currentSlackAttributionInfo = () => ({
219700
+ botName: agentName,
219701
+ botUrl: this.agentLink(agentId),
219702
+ runtime: this.runtimeNames[agent.runtime] ?? agent.runtime,
219703
+ model: this.buildStatusInfo(p).model ?? turnModel ?? "default",
219704
+ sessionUrl: this.sessionLink(sessionId)
219705
+ });
219706
+ if (p.platform === "slack") {
219707
+ const attribution = buildAttributionBlocks(currentSlackAttributionInfo());
219708
+ p.attribution = {
219709
+ blocks: attribution.blocks,
219710
+ key: JSON.stringify(attribution.blocks)
219711
+ };
219712
+ }
219575
219713
  if (!wasRunning) this.showActivity(replyConn, msg.channel, statusThread, "is thinking…");
219576
219714
  this.emitStatusBar(p);
219577
219715
  const { stopReason, usage } = await host.prompt(sessionId, blocks);
@@ -219626,14 +219764,15 @@ var Daemon = class Daemon {
219626
219764
  }
219627
219765
  } else {
219628
219766
  const link = this.sessionLink(sessionId);
219629
- const finals = conv instanceof TelegramConverger || conv instanceof DiscordConverger ? conv.onFinal(link) : conv.onFinal({
219630
- botName: agentName,
219631
- botUrl: this.agentLink(agentId),
219632
- runtime: this.runtimeNames[agent.runtime] ?? agent.runtime,
219633
- model: this.buildStatusInfo(p).model ?? turnModel ?? "default",
219634
- sessionUrl: link,
219635
- shared: this.isSharedSlackIntegration(agentId, integrationId)
219636
- });
219767
+ const finalSlackAttributionInfo = currentSlackAttributionInfo();
219768
+ if (p.platform === "slack") {
219769
+ const attribution = buildAttributionBlocks(finalSlackAttributionInfo);
219770
+ p.attribution = {
219771
+ blocks: attribution.blocks,
219772
+ key: JSON.stringify(attribution.blocks)
219773
+ };
219774
+ }
219775
+ const finals = conv instanceof TelegramConverger || conv instanceof DiscordConverger ? conv.onFinal(link) : conv.onFinal(finalSlackAttributionInfo);
219637
219776
  for (const action of finals) this.enqueueApply(p, action);
219638
219777
  }
219639
219778
  for (const ev of rec.onFinal()) this.recordEvent(agentId, msg.channel, statusThread, ev);
@@ -219679,7 +219818,8 @@ var Daemon = class Daemon {
219679
219818
  }
219680
219819
  if (!covered) this.enqueueApply(p, {
219681
219820
  kind: "post",
219682
- text: `⚠️ Agent failed to respond: ${reason}`
219821
+ text: `⚠️ Agent failed to respond: ${reason}`,
219822
+ attributed: false
219683
219823
  });
219684
219824
  this.showActivity(replyConn, msg.channel, statusThread, "");
219685
219825
  await p.applyChain;
@@ -219692,6 +219832,7 @@ var Daemon = class Daemon {
219692
219832
  } finally {
219693
219833
  this.clearCancelBackstop(agentId, sessionId);
219694
219834
  if (p.outputSuppressed) await p.applyChain.catch(() => {});
219835
+ if (p.platform === "slack" && p.conn && p.staleReplyFooters?.length) await this.clearStaleSlackReplyFooters(p.conn, p);
219695
219836
  releaseReplyConn();
219696
219837
  this.clearIdle(p);
219697
219838
  this.releasePermissions(agentId, sessionId);
@@ -219873,11 +220014,11 @@ var Daemon = class Daemon {
219873
220014
  * Apply one converger action against the session's Slack connection:
219874
220015
  * - set-status → assistant.threads.setStatus (best-effort; '' clears)
219875
220016
  * - set-title → assistant.threads.setTitle (best-effort; app DMs only)
219876
- * - post → a new thread message; ALSO recorded to the transcript as the
219877
- * agent's reply text (sender = agentId), so other agents replaying the thread
219878
- * see what it said. Keyed on `statusThread` (the §8.5 replay key).
220017
+ * - post → a new thread message born with the linked footer; ALSO recorded
220018
+ * to the transcript as the agent's reply text (sender = agentId), so other agents
220019
+ * replaying the thread see what it said. Keyed on `statusThread` (§8.5).
219879
220020
  * - notice → posted to the thread but NOT recorded (system chrome).
219880
- * - attribution → folds trailing footer blocks into the last successful `post`.
220021
+ * - attribution → closes the footer lifecycle and retries stale-section cleanup.
219881
220022
  * - progress / plan → the single in-place message of that kind, posted once
219882
220023
  * then chat.update-ed (§9.1 就地更新). The first post's ts is remembered on `p`.
219883
220024
  */
@@ -219888,6 +220029,25 @@ var Daemon = class Daemon {
219888
220029
  ...p.iconUrl ? { icon_url: p.iconUrl } : {}
219889
220030
  };
219890
220031
  }
220032
+ /** Remove attribution blocks from older body sections. Slack edits are best-effort,
220033
+ * so retain failed rows for the next body post and the finalization retry. Test fakes
220034
+ * historically return void; only an explicit `false` means the update failed. */
220035
+ async clearStaleSlackReplyFooters(conn, p, additional = []) {
220036
+ const pending = [...p.staleReplyFooters ?? [], ...additional];
220037
+ if (pending.length === 0) return;
220038
+ delete p.staleReplyFooters;
220039
+ const failed = [];
220040
+ for (const reply of new Map(pending.map((item) => [item.ts, item])).values()) try {
220041
+ if (await conn.updateBlocks(p.channel, reply.ts, [{
220042
+ type: "markdown",
220043
+ text: reply.text
220044
+ }]) === false) failed.push(reply);
220045
+ } catch (err) {
220046
+ failed.push(reply);
220047
+ this.log.debug(`slack: stale footer cleanup failed (${reply.ts}): ${formatErr(err)}`);
220048
+ }
220049
+ if (failed.length > 0) p.staleReplyFooters = failed;
220050
+ }
219891
220051
  async applySlackAction(p, action) {
219892
220052
  const conn = p.conn;
219893
220053
  if (!conn) {
@@ -219910,11 +220070,25 @@ var Daemon = class Daemon {
219910
220070
  if (p.statusThread) await conn.setTitle(p.channel, p.statusThread, action.text);
219911
220071
  return;
219912
220072
  case "post": {
219913
- const ts = await conn.postMessage(p.channel, action.text, p.thread, postOptions);
219914
- if (ts) p.lastReply = {
219915
- ts,
219916
- text: action.text
219917
- };
220073
+ const trackReply = action.attributed !== false;
220074
+ const previous = trackReply ? p.lastReply : void 0;
220075
+ const attribution = trackReply ? p.attribution : void 0;
220076
+ const replyOptions = attribution ? {
220077
+ ...postOptions,
220078
+ trailingBlocks: attribution.blocks
220079
+ } : postOptions;
220080
+ const ts = await conn.postMessage(p.channel, action.text, p.thread, replyOptions);
220081
+ if (ts && trackReply) {
220082
+ if (attribution) await this.clearStaleSlackReplyFooters(conn, p, previous?.footerKey ? [{
220083
+ ts: previous.ts,
220084
+ text: previous.text
220085
+ }] : []);
220086
+ p.lastReply = {
220087
+ ts,
220088
+ text: action.text,
220089
+ ...attribution ? { footerKey: attribution.key } : {}
220090
+ };
220091
+ }
219918
220092
  this.store.appendTranscript({
219919
220093
  channel: p.channel,
219920
220094
  thread: p.statusThread,
@@ -219930,10 +220104,11 @@ var Daemon = class Daemon {
219930
220104
  await conn.postMessage(p.channel, action.text, p.thread, postOptions);
219931
220105
  return;
219932
220106
  case "attribution":
219933
- if (p.lastReply) await conn.updateBlocks(p.channel, p.lastReply.ts, [{
219934
- type: "markdown",
219935
- text: p.lastReply.text
219936
- }, ...action.blocks]);
220107
+ p.attribution = {
220108
+ blocks: action.blocks,
220109
+ key: JSON.stringify(action.blocks)
220110
+ };
220111
+ await this.clearStaleSlackReplyFooters(conn, p);
219937
220112
  return;
219938
220113
  case "progress":
219939
220114
  if (p.progressTs) await conn.updateMessage(p.channel, p.progressTs, action.text);
@@ -219970,7 +220145,8 @@ var Daemon = class Daemon {
219970
220145
  await conn.updateBlocks(p.channel, ts, action.blocks, action.text);
219971
220146
  } else if (!p.statusBarAttempted) {
219972
220147
  p.statusBarAttempted = true;
219973
- const posted = await conn.postBlocks(p.channel, action.blocks, action.text, p.thread, postOptions);
220148
+ const statusPostOptions = this.isSharedSlackIntegration(p.agentId, p.integrationId) ? void 0 : postOptions;
220149
+ const posted = await conn.postBlocks(p.channel, action.blocks, action.text, p.thread, statusPostOptions);
219974
220150
  if (posted) {
219975
220151
  p.statusBarTs = posted;
219976
220152
  this.store.setStatusBarTs(p.sessionKey, posted);
@@ -220144,15 +220320,14 @@ var Daemon = class Daemon {
220144
220320
  const orgSeg = this.cpOrgSlug ? `/${encodeURIComponent(this.cpOrgSlug)}` : "";
220145
220321
  return `${this.webAppBase()}${orgSeg}/sessions/${encodeURIComponent(acpSessionId)}`;
220146
220322
  }
220147
- /** The console deep link to an agent: `<base>/<orgSlug>/agents/<agentId>` — the
220148
- * shared-bot footer's "sent by <agent_name>" link (§10.1). Same org-seg rule as
220149
- * {@link sessionLink}. */
220323
+ /** The console deep link to an agent: `<base>/<orgSlug>/agents/<agentId>`. Same
220324
+ * org-segment rule as {@link sessionLink}. */
220150
220325
  agentLink(agentId) {
220151
220326
  const orgSeg = this.cpOrgSlug ? `/${encodeURIComponent(this.cpOrgSlug)}` : "";
220152
220327
  return `${this.webAppBase()}${orgSeg}/agents/${encodeURIComponent(agentId)}`;
220153
220328
  }
220154
- /** Whether this turn used a shared Slack integration. Shared mode renders the same
220155
- * dynamic footer text in a section so it can retain the config-button accessory. */
220329
+ /** Whether this turn's reply rides a shared Slack integration. The relay owns that
220330
+ * app's Socket Mode connection, so interactive actions must use relay-recognized ids. */
220156
220331
  isSharedSlackIntegration(agentId, integrationId) {
220157
220332
  if (!integrationId) return false;
220158
220333
  const int = this.agents.get(agentId)?.integrations.find((i) => i.id === integrationId);
@@ -220311,10 +220486,21 @@ var Daemon = class Daemon {
220311
220486
  else {
220312
220487
  p.lastStatusBar = key;
220313
220488
  const link = this.sessionLink(p.acpSessionId);
220489
+ const shared = p.integrationId && this.isSharedSlackIntegration(p.agentId, p.integrationId) ? {
220490
+ sessionTarget: encodeSharedSlackStatusTarget({
220491
+ agentId: p.agentId,
220492
+ integrationId: p.integrationId,
220493
+ sessionKey: p.sessionKey
220494
+ }),
220495
+ currentAgent: {
220496
+ id: p.agentId,
220497
+ name: p.agentName
220498
+ }
220499
+ } : void 0;
220314
220500
  this.enqueueApply(p, {
220315
220501
  kind: "status-bar",
220316
220502
  text: renderStatusBar(info),
220317
- blocks: buildStatusBlocks(info, p.sessionKey, link)
220503
+ blocks: buildStatusBlocks(info, p.sessionKey, link, shared)
220318
220504
  });
220319
220505
  }
220320
220506
  }
@@ -221846,4 +222032,4 @@ async function runForeground(opts, deps = {}) {
221846
222032
  //#endregion
221847
222033
  export { ClientTransport as i, CP_SUBPROTOCOL as n, CP_WS_PATH as r, runForeground as t };
221848
222034
 
221849
- //# sourceMappingURL=run-foreground-BqC40mtd.js.map
222035
+ //# sourceMappingURL=run-foreground-CS56Yp2s.js.map