@botiverse/raft-daemon 0.58.2 → 0.59.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.
@@ -14166,7 +14166,7 @@ function resolveSlockCliPathOrEmpty(moduleUrl = import.meta.url) {
14166
14166
  }
14167
14167
  async function runBundledSlockCli(argv) {
14168
14168
  process.argv = [process.execPath, "slock", ...argv];
14169
- await import("./dist-ADWW7UHH.js");
14169
+ await import("./dist-CLQ4P4XZ.js");
14170
14170
  }
14171
14171
  function detectRuntimes(tracer = noopTracer) {
14172
14172
  const ids = [];
package/dist/cli/index.js CHANGED
@@ -158,6 +158,7 @@ function emitCliTransportNormalizedError(attrs, env = process.env) {
158
158
  function routeFamilyForPath(pathname) {
159
159
  const normalized = pathname.split("?")[0] || "/";
160
160
  if (normalized === "/internal/agent-api/send") return "agent-api/send";
161
+ if (normalized === "/internal/agent-api/activity") return "agent-api/activity";
161
162
  if (normalized === "/internal/agent-api/events") return "agent-api/events";
162
163
  if (normalized === "/internal/agent-api/inbox") return "agent-api/inbox";
163
164
  if (normalized === "/internal/agent-api/receive-ack") return "agent-api/events";
@@ -317,6 +318,9 @@ var ApiClient = class {
317
318
  if (suffix === "/reminders" || suffix.startsWith("/reminders?") || suffix.startsWith("/reminders/")) {
318
319
  return `/internal/agent-api${suffix}`;
319
320
  }
321
+ if (suffix === "/mention-actions/pending" || suffix === "/mention-actions/execute") {
322
+ return `/internal/agent-api${suffix}`;
323
+ }
320
324
  if (suffix === "/receive" || suffix.startsWith("/receive?")) {
321
325
  return "/internal/agent-api/events?since=latest";
322
326
  }
@@ -15720,6 +15724,11 @@ var SERVER_CAPABILITY_MATRIX = {
15720
15724
  };
15721
15725
 
15722
15726
  // ../shared/src/index.ts
15727
+ var EXTERNAL_AGENT_ACTIVITY_EVENT_SCHEMA = "raft-activity.v1";
15728
+ var EXTERNAL_AGENT_ACTIVITY_DRAIN_SCHEMA = "raft-activity-drain.v1";
15729
+ var EXTERNAL_AGENT_ACTIVITY_INGEST_SCHEMA = "raft-agent-activity-ingest.v1";
15730
+ var EXTERNAL_AGENT_ACTIVITY_TEXT_LIMIT = 4096;
15731
+ var EXTERNAL_AGENT_ACTIVITY_TOOL_NAME_LIMIT = 120;
15723
15732
  var EXTERNAL_AGENT_RUNTIME_ID = "external";
15724
15733
  var EXTERNAL_AGENT_RUNTIME_MODEL = "external";
15725
15734
  var EXTERNAL_AGENT_RUNTIME_DISPLAY_NAME = "External agent";
@@ -16016,6 +16025,15 @@ async function runAgentCommsBridgeOnce(input) {
16016
16025
  }
16017
16026
  const fetchedLastSeq = typeof fetched.last_seen_hint_seq === "number" ? fetched.last_seen_hint_seq : typeof fetched.last_hint_seq === "number" ? fetched.last_hint_seq : void 0;
16018
16027
  if (typeof fetchedLastSeq === "number") lastSeenHintSeq = Math.max(lastSeenHintSeq ?? 0, fetchedLastSeq);
16028
+ if (input.activitySource && input.activitySink) {
16029
+ output.push(await drainAndForwardActivity({
16030
+ identity,
16031
+ source: input.activitySource,
16032
+ sink: input.activitySink,
16033
+ max: input.activityDrainLimit ?? input.limit ?? 50,
16034
+ now
16035
+ }));
16036
+ }
16019
16037
  store.writeSession({
16020
16038
  coreSessionId,
16021
16039
  ...typeof lastSeenHintSeq === "number" ? { lastSeenHintSeq } : {}
@@ -16023,6 +16041,126 @@ async function runAgentCommsBridgeOnce(input) {
16023
16041
  output.push(lifecycle(identity, processedHintCount > 0 ? "handoff_pending" : "listening_idle", now));
16024
16042
  return output;
16025
16043
  }
16044
+ async function drainAndForwardActivity(input) {
16045
+ let drained;
16046
+ try {
16047
+ drained = await input.source.drainActivity({ max: input.max });
16048
+ } catch (err) {
16049
+ return activityDrainEvent(input.identity, input.now, {
16050
+ outcome: "failed",
16051
+ drainedCount: 0,
16052
+ forwardedCount: 0,
16053
+ rejectedCount: 0,
16054
+ droppedCount: 0,
16055
+ errorClass: err instanceof Error ? err.name : typeof err,
16056
+ errorMessage: errorMessage2(err)
16057
+ });
16058
+ }
16059
+ if (drained.schema !== EXTERNAL_AGENT_ACTIVITY_DRAIN_SCHEMA || !Array.isArray(drained.events)) {
16060
+ return activityDrainEvent(input.identity, input.now, {
16061
+ outcome: "failed",
16062
+ drainedCount: 0,
16063
+ forwardedCount: 0,
16064
+ rejectedCount: 0,
16065
+ droppedCount: normalizeNonNegativeInteger(drained.dropped),
16066
+ errorClass: "ProtocolError",
16067
+ errorMessage: "activity drain response did not match raft-activity-drain.v1"
16068
+ });
16069
+ }
16070
+ const sanitized = sanitizeExternalAgentActivityEvents(drained.events, input.now);
16071
+ if (sanitized.events.length === 0) {
16072
+ return activityDrainEvent(input.identity, input.now, {
16073
+ outcome: "no_events",
16074
+ drainedCount: drained.events.length,
16075
+ forwardedCount: 0,
16076
+ rejectedCount: sanitized.rejectedCount,
16077
+ droppedCount: normalizeNonNegativeInteger(drained.dropped)
16078
+ });
16079
+ }
16080
+ try {
16081
+ await input.sink.forwardActivity({
16082
+ schema: EXTERNAL_AGENT_ACTIVITY_INGEST_SCHEMA,
16083
+ coreSessionId: input.identity.coreSessionId,
16084
+ adapterInstance: input.identity.adapterInstance,
16085
+ events: sanitized.events,
16086
+ dropped: normalizeNonNegativeInteger(drained.dropped)
16087
+ });
16088
+ } catch (err) {
16089
+ return activityDrainEvent(input.identity, input.now, {
16090
+ outcome: "failed",
16091
+ drainedCount: drained.events.length,
16092
+ forwardedCount: 0,
16093
+ rejectedCount: sanitized.rejectedCount,
16094
+ droppedCount: normalizeNonNegativeInteger(drained.dropped),
16095
+ errorClass: err instanceof Error ? err.name : typeof err,
16096
+ errorMessage: errorMessage2(err)
16097
+ });
16098
+ }
16099
+ return activityDrainEvent(input.identity, input.now, {
16100
+ outcome: "forwarded",
16101
+ drainedCount: drained.events.length,
16102
+ forwardedCount: sanitized.events.length,
16103
+ rejectedCount: sanitized.rejectedCount,
16104
+ droppedCount: normalizeNonNegativeInteger(drained.dropped)
16105
+ });
16106
+ }
16107
+ function sanitizeExternalAgentActivityEvents(rawEvents, now = () => /* @__PURE__ */ new Date()) {
16108
+ const events = [];
16109
+ let rejectedCount = 0;
16110
+ for (const raw of rawEvents) {
16111
+ const hookEventName = stringField(raw.hookEventName ?? raw.hook_event_name, 80);
16112
+ if (!hookEventName) {
16113
+ rejectedCount += 1;
16114
+ continue;
16115
+ }
16116
+ const eventId = stringField(raw.eventId ?? raw.event_id, 160) ?? `event_${randomUUID()}`;
16117
+ const sessionId = stringField(raw.sessionId ?? raw.session_id, 200);
16118
+ const toolName = stringField(raw.toolName ?? raw.tool_name, EXTERNAL_AGENT_ACTIVITY_TOOL_NAME_LIMIT);
16119
+ const status = stringField(raw.status, 40);
16120
+ const errorClass = stringField(raw.errorClass ?? raw.error_class, 120);
16121
+ const occurredAt = validIsoDate(raw.occurredAt ?? raw.occurred_at) ?? now().toISOString();
16122
+ const durationMs = normalizeNonNegativeInteger(raw.durationMs ?? raw.duration_ms);
16123
+ const toolInput = truncateActivityText(
16124
+ raw.toolInput ?? raw.tool_input,
16125
+ raw.toolInputTruncated ?? raw.tool_input_truncated ?? raw.truncated
16126
+ );
16127
+ const toolOutput = truncateActivityText(
16128
+ raw.toolOutput ?? raw.tool_output,
16129
+ raw.toolOutputTruncated ?? raw.tool_output_truncated ?? raw.truncated
16130
+ );
16131
+ events.push({
16132
+ schema: EXTERNAL_AGENT_ACTIVITY_EVENT_SCHEMA,
16133
+ eventId,
16134
+ ...sessionId ? { sessionId } : {},
16135
+ hookEventName,
16136
+ ...toolName ? { toolName } : {},
16137
+ ...status ? { status } : {},
16138
+ occurredAt,
16139
+ ...durationMs > 0 ? { durationMs } : {},
16140
+ ...errorClass ? { errorClass } : {},
16141
+ ...toolInput.text !== void 0 ? { toolInput: toolInput.text, toolInputTruncated: toolInput.truncated } : {},
16142
+ ...toolOutput.text !== void 0 ? { toolOutput: toolOutput.text, toolOutputTruncated: toolOutput.truncated } : {}
16143
+ });
16144
+ }
16145
+ return { events, rejectedCount };
16146
+ }
16147
+ function activityDrainEvent(identity, now, result2) {
16148
+ return {
16149
+ type: "agent_comms.activity_drain",
16150
+ eventId: `event_${randomUUID()}`,
16151
+ protocolVersion: AGENT_COMMS_PROTOCOL_VERSION,
16152
+ timestamp: now().toISOString(),
16153
+ coreSessionId: identity.coreSessionId,
16154
+ agentId: identity.agentId,
16155
+ profile: identity.profileSlug,
16156
+ profileSlug: identity.profileSlug,
16157
+ adapterInstance: identity.adapterInstance,
16158
+ runtimeSession: null,
16159
+ source: "slock-agent-bridge",
16160
+ provenance: commsCoreProvenance("comms_core"),
16161
+ ...result2
16162
+ };
16163
+ }
16026
16164
  var BRIDGE_LOG_MAX_BYTES = 5 * 1024 * 1024;
16027
16165
  function rotateBridgeLogIfNeeded(logFile) {
16028
16166
  try {
@@ -16352,6 +16490,46 @@ function stripContentFields(wakeHint) {
16352
16490
  delete clone2.message;
16353
16491
  return clone2;
16354
16492
  }
16493
+ function stringField(value, maxLength) {
16494
+ if (typeof value !== "string") return void 0;
16495
+ const trimmed = value.replace(/\s+/g, " ").trim();
16496
+ if (!trimmed) return void 0;
16497
+ return trimmed.slice(0, maxLength);
16498
+ }
16499
+ function validIsoDate(value) {
16500
+ if (typeof value !== "string") return void 0;
16501
+ const parsed = Date.parse(value);
16502
+ if (Number.isNaN(parsed)) return void 0;
16503
+ return new Date(parsed).toISOString();
16504
+ }
16505
+ function normalizeNonNegativeInteger(value) {
16506
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return 0;
16507
+ return Math.floor(value);
16508
+ }
16509
+ function truncateActivityText(value, alreadyTruncated) {
16510
+ if (value === void 0 || value === null) return { truncated: Boolean(alreadyTruncated) };
16511
+ let text;
16512
+ if (typeof value === "string") {
16513
+ text = value;
16514
+ } else {
16515
+ try {
16516
+ text = JSON.stringify(value);
16517
+ } catch {
16518
+ text = String(value);
16519
+ }
16520
+ }
16521
+ const marker = "\n[truncated]";
16522
+ if (text.length > EXTERNAL_AGENT_ACTIVITY_TEXT_LIMIT) {
16523
+ return {
16524
+ text: `${text.slice(0, Math.max(0, EXTERNAL_AGENT_ACTIVITY_TEXT_LIMIT - marker.length))}${marker}`,
16525
+ truncated: true
16526
+ };
16527
+ }
16528
+ return { text, truncated: Boolean(alreadyTruncated) };
16529
+ }
16530
+ function errorMessage2(err) {
16531
+ return (err instanceof Error ? err.message : String(err)).slice(0, 200);
16532
+ }
16355
16533
  function safePathSegment(value) {
16356
16534
  return value.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 120) || "unknown";
16357
16535
  }
@@ -16509,7 +16687,10 @@ var agentBridgeCommand = defineCommand(
16509
16687
  { flags: "--wake-channel-endpoint <url>", description: "Localhost wake endpoint exposed by the runtime's Raft channel plugin (see docs/wake-endpoint-contract.md in raft-external-agents)." },
16510
16688
  { flags: "--wake-channel-token <token>", description: "Optional shared token for the Raft channel wake endpoint (env: RAFT_CHANNEL_TOKEN)." },
16511
16689
  { flags: "--runtime-session <id>", description: "Optional runtime session id when the adapter endpoint does not return one." },
16512
- { flags: "--reconcile-interval-ms <ms>", description: "Re-peek the full server-pending set every <ms> and re-wake hints that were injected but never consumed (0 disables; default 120000)." }
16690
+ { flags: "--reconcile-interval-ms <ms>", description: "Re-peek the full server-pending set every <ms> and re-wake hints that were injected but never consumed (0 disables; default 120000)." },
16691
+ { flags: "--activity-channel-endpoint <url>", description: "Localhost activity drain endpoint exposed by the runtime's Raft channel plugin. Defaults to /activity/drain derived from --wake-channel-endpoint." },
16692
+ { flags: "--activity-channel-token <token>", description: "Optional shared token for the activity drain endpoint (env: RAFT_CHANNEL_TOKEN)." },
16693
+ { flags: "--activity-drain-limit <n>", description: "Maximum plugin activity events to drain per bridge iteration." }
16513
16694
  ]
16514
16695
  },
16515
16696
  async (ctx, options) => {
@@ -16524,10 +16705,13 @@ var agentBridgeCommand = defineCommand(
16524
16705
  const pollIntervalMs = parsePositiveInt(options.pollIntervalMs, 5e3, "poll-interval-ms");
16525
16706
  const reconcileIntervalMs = options.reconcileIntervalMs === "0" ? 0 : parsePositiveInt(options.reconcileIntervalMs, 12e4, "reconcile-interval-ms");
16526
16707
  const limit = parsePositiveInt(options.limit, 50, "limit");
16708
+ const activityDrainLimit = parsePositiveInt(options.activityDrainLimit, 50, "activity-drain-limit");
16527
16709
  const client = ctx.createApiClient(agentContext);
16528
16710
  const pollSource = createAgentApiWakeHintSource(client);
16529
16711
  const streamSource = options.once ? null : createAgentApiWakeHintStreamSource(client, ctx.env);
16530
16712
  const wakeAdapter = createWakeAdapter(options, ctx.env);
16713
+ const activitySource = createActivityDrainSource(options, ctx.env);
16714
+ const activitySink = activitySource ? createAgentApiActivitySink(client) : void 0;
16531
16715
  let bridgeLock;
16532
16716
  try {
16533
16717
  bridgeLock = acquireAgentCommsBridgeLock({
@@ -16581,6 +16765,9 @@ var agentBridgeCommand = defineCommand(
16581
16765
  replayPending,
16582
16766
  wakeAdapter,
16583
16767
  runtimeSession: options.runtimeSession ?? null,
16768
+ activitySource,
16769
+ activitySink,
16770
+ activityDrainLimit,
16584
16771
  recentInjections
16585
16772
  });
16586
16773
  for (const event of events) emit(event);
@@ -16863,6 +17050,59 @@ function createWakeAdapter(options, env = process.env) {
16863
17050
  token: options.wakeChannelToken ?? env.RAFT_CHANNEL_TOKEN
16864
17051
  });
16865
17052
  }
17053
+ function createActivityDrainSource(options, env = process.env) {
17054
+ const endpointUrl = options.activityChannelEndpoint ?? deriveActivityDrainEndpoint(options.wakeChannelEndpoint);
17055
+ if (!endpointUrl) return void 0;
17056
+ const token = options.activityChannelToken ?? options.wakeChannelToken ?? env.RAFT_CHANNEL_TOKEN;
17057
+ return {
17058
+ async drainActivity(input) {
17059
+ const url2 = new URL(endpointUrl);
17060
+ url2.searchParams.set("max", String(input.max));
17061
+ const signal = typeof AbortSignal.timeout === "function" ? AbortSignal.timeout(3e3) : void 0;
17062
+ const response = await fetch(url2, {
17063
+ method: "GET",
17064
+ signal,
17065
+ headers: {
17066
+ ...token ? { "x-raft-bridge-token": token } : {}
17067
+ }
17068
+ });
17069
+ const contentType = response.headers.get("content-type") ?? "";
17070
+ const data = contentType.includes("application/json") ? await response.json() : {};
17071
+ if (!response.ok) {
17072
+ throw new CliError({
17073
+ code: response.status >= 500 ? "SERVER_5XX" : "BRIDGE_ACTIVITY_DRAIN_FAILED",
17074
+ message: typeof data?.error === "string" ? data.error : `HTTP ${response.status}`
17075
+ });
17076
+ }
17077
+ return data;
17078
+ }
17079
+ };
17080
+ }
17081
+ function createAgentApiActivitySink(client) {
17082
+ return {
17083
+ async forwardActivity(input) {
17084
+ const response = await client.request("POST", "/internal/agent-api/activity", input);
17085
+ if (!response.ok) {
17086
+ throw new CliError({
17087
+ code: response.status >= 500 ? "SERVER_5XX" : "BRIDGE_ACTIVITY_FORWARD_FAILED",
17088
+ message: response.error ?? `HTTP ${response.status}`
17089
+ });
17090
+ }
17091
+ }
17092
+ };
17093
+ }
17094
+ function deriveActivityDrainEndpoint(wakeChannelEndpoint) {
17095
+ if (!wakeChannelEndpoint) return void 0;
17096
+ try {
17097
+ const url2 = new URL(wakeChannelEndpoint);
17098
+ url2.pathname = "/activity/drain";
17099
+ url2.search = "";
17100
+ url2.hash = "";
17101
+ return url2.toString();
17102
+ } catch {
17103
+ return void 0;
17104
+ }
17105
+ }
16866
17106
  function sleep(ms) {
16867
17107
  return new Promise((resolve) => setTimeout(resolve, ms));
16868
17108
  }
@@ -17776,6 +18016,86 @@ ${opts.heldAction} Review the bounded context shown here, then choose one path.$
17776
18016
  ` + (opts.draftInstructions ?? "") + continueAnyway;
17777
18017
  }
17778
18018
 
18019
+ // src/commands/mention/_format.ts
18020
+ function normalizeAction(action) {
18021
+ if (action === "notify" || action === "notify_only") return "notify";
18022
+ if (action === "invite" || action === "add") return "invite";
18023
+ return null;
18024
+ }
18025
+ function formatActionCommands(action) {
18026
+ const verbs = action.availableActions.map(normalizeAction).filter((verb) => verb !== null);
18027
+ return Array.from(new Set(verbs)).map((verb) => ` ${verb}: slock mention ${verb} ${action.resolutionId}`);
18028
+ }
18029
+ function normalizePendingMentionActions(data) {
18030
+ const value = data;
18031
+ const raw = Array.isArray(value?.pendingMentionActions) ? value.pendingMentionActions : Array.isArray(value?.actions) ? value.actions : Array.isArray(value?.results) ? value.results : [];
18032
+ return raw.filter((item) => Boolean(item && typeof item === "object")).map((item) => ({
18033
+ resolutionId: String(item.resolutionId ?? item.id ?? ""),
18034
+ messageId: String(item.messageId ?? ""),
18035
+ targetType: String(item.targetType ?? "unknown"),
18036
+ targetHandle: String(item.targetHandle ?? ""),
18037
+ reason: String(item.reason ?? "Mention target was not notified at send time."),
18038
+ availableActions: Array.isArray(item.availableActions) ? item.availableActions.map(String) : [],
18039
+ expiresAt: typeof item.expiresAt === "string" ? item.expiresAt : null
18040
+ })).filter((item) => item.resolutionId.length > 0);
18041
+ }
18042
+ function normalizeMentionActionResults(data) {
18043
+ const value = data;
18044
+ const raw = Array.isArray(value?.results) ? value.results : Array.isArray(value?.actionResults) ? value.actionResults : [];
18045
+ return raw.filter((item) => Boolean(item && typeof item === "object")).map((item) => ({
18046
+ resolutionId: String(item.resolutionId ?? item.id ?? ""),
18047
+ status: String(item.status ?? "not_found"),
18048
+ action: typeof item.action === "string" ? normalizeAction(item.action) : null,
18049
+ messageId: typeof item.messageId === "string" ? item.messageId : null,
18050
+ channelId: typeof item.channelId === "string" ? item.channelId : null,
18051
+ targetType: typeof item.targetType === "string" ? item.targetType : null,
18052
+ targetId: typeof item.targetId === "string" ? item.targetId : null,
18053
+ targetHandle: typeof item.targetHandle === "string" ? item.targetHandle : null,
18054
+ message: typeof item.message === "string" ? item.message : null,
18055
+ reason: typeof item.reason === "string" ? item.reason : null,
18056
+ dedupedResolutionIds: Array.isArray(item.dedupedResolutionIds) ? item.dedupedResolutionIds.filter((id) => typeof id === "string" && id.length > 0) : []
18057
+ })).filter((item) => item.resolutionId.length > 0);
18058
+ }
18059
+ function formatPendingMentionActions(actions, opts = {}) {
18060
+ if (actions.length === 0) {
18061
+ return opts.source === "pending" ? "Pending mention actions\n\nNo pending mention actions.\n" : "";
18062
+ }
18063
+ const lines = ["Pending mention actions", ""];
18064
+ for (const action of actions) {
18065
+ const target = action.targetHandle ? `${action.targetHandle} (${action.targetType})` : action.targetType;
18066
+ lines.push(`- ${action.resolutionId} \u2014 ${target}`);
18067
+ if (action.messageId) lines.push(` message: ${action.messageId}`);
18068
+ lines.push(` reason: ${action.reason}`);
18069
+ if (action.expiresAt) lines.push(` expires: ${action.expiresAt}`);
18070
+ const commands = formatActionCommands(action);
18071
+ if (commands.length > 0) {
18072
+ lines.push(" available commands:");
18073
+ lines.push(...commands);
18074
+ }
18075
+ }
18076
+ return `${lines.join("\n")}
18077
+ `;
18078
+ }
18079
+ function formatMentionActionResults(action, results) {
18080
+ const lines = [`Mention ${action} results`, ""];
18081
+ if (results.length === 0) {
18082
+ lines.push("No result rows returned.");
18083
+ return `${lines.join("\n")}
18084
+ `;
18085
+ }
18086
+ for (const result2 of results) {
18087
+ const target = result2.targetHandle ? ` ${result2.targetHandle}` : "";
18088
+ const detail = result2.message ?? result2.reason;
18089
+ const suffix = detail ? ` \u2014 ${detail}` : "";
18090
+ lines.push(`- ${result2.resolutionId}${target}: ${result2.status}${suffix}`);
18091
+ if (result2.dedupedResolutionIds && result2.dedupedResolutionIds.length > 1) {
18092
+ lines.push(` deduped: ${result2.dedupedResolutionIds.join(", ")}`);
18093
+ }
18094
+ }
18095
+ return `${lines.join("\n")}
18096
+ `;
18097
+ }
18098
+
17779
18099
  // src/commands/message/_continueDraftState.ts
17780
18100
  import fs4 from "fs";
17781
18101
  import os3 from "os";
@@ -18084,7 +18404,11 @@ var messageSendCommand = defineCommand(
18084
18404
  --- New messages you may have missed ---
18085
18405
  ${formatMessages(data.recentUnread)}`;
18086
18406
  }
18087
- writeText(ctx.io, `Message sent to ${opts.target}. Message ID: ${data.messageId}${replyHint}${unreadSection}
18407
+ const pendingMentionActions = normalizePendingMentionActions(data);
18408
+ const mentionSection = pendingMentionActions.length > 0 ? `
18409
+
18410
+ ${formatPendingMentionActions(pendingMentionActions, { source: "send" }).trimEnd()}` : "";
18411
+ writeText(ctx.io, `Message sent to ${opts.target}. Message ID: ${data.messageId}${replyHint}${mentionSection}${unreadSection}
18088
18412
  `);
18089
18413
  }
18090
18414
  );
@@ -19174,6 +19498,83 @@ function registerTaskUpdateCommand(parent, runtimeOptions) {
19174
19498
  registerCliCommand(parent, taskUpdateCommand, runtimeOptions);
19175
19499
  }
19176
19500
 
19501
+ // src/commands/mention/execute.ts
19502
+ function normalizeResolutionIds(rawIds) {
19503
+ const ids = (rawIds ?? []).map((id) => id.trim()).filter(Boolean);
19504
+ if (ids.length === 0) {
19505
+ throw cliError("INVALID_ARG", "At least one resolution id is required.");
19506
+ }
19507
+ return ids;
19508
+ }
19509
+ function buildMentionExecuteCommand(action) {
19510
+ return defineCommand(
19511
+ {
19512
+ name: action,
19513
+ description: `${action === "notify" ? "Notify" : "Invite"} unresolved mention targets by resolution id`,
19514
+ arguments: ["<resolutionIds...>"],
19515
+ options: [{ flags: "--json", description: "Emit machine-readable JSON" }]
19516
+ },
19517
+ async (ctx, resolutionIds, opts = {}) => {
19518
+ const ids = normalizeResolutionIds(resolutionIds);
19519
+ const agentContext = ctx.loadAgentContext();
19520
+ const client = ctx.createApiClient(agentContext);
19521
+ const res = await client.request(
19522
+ "POST",
19523
+ `/internal/agent/${encodeURIComponent(agentContext.agentId)}/mention-actions/execute`,
19524
+ { action, resolutionIds: ids }
19525
+ );
19526
+ if (!res.ok || !res.data) {
19527
+ throw cliError(res.status >= 500 ? "SERVER_5XX" : "MENTION_ACTION_FAILED", res.error ?? `HTTP ${res.status}`);
19528
+ }
19529
+ const results = normalizeMentionActionResults(res.data);
19530
+ if (opts.json) {
19531
+ writeJson(ctx.io, { ok: true, action, results });
19532
+ return;
19533
+ }
19534
+ writeText(ctx.io, formatMentionActionResults(action, results));
19535
+ }
19536
+ );
19537
+ }
19538
+ function registerMentionExecuteCommands(parent, runtimeOptions = {}) {
19539
+ registerCliCommand(parent, buildMentionExecuteCommand("notify"), runtimeOptions);
19540
+ registerCliCommand(parent, buildMentionExecuteCommand("invite"), runtimeOptions);
19541
+ }
19542
+
19543
+ // src/commands/mention/pending.ts
19544
+ var mentionPendingCommand = defineCommand(
19545
+ {
19546
+ name: "pending",
19547
+ description: "List sender-side pending mention actions",
19548
+ options: [{ flags: "--json", description: "Emit machine-readable JSON" }]
19549
+ },
19550
+ async (ctx, opts = {}) => {
19551
+ const agentContext = ctx.loadAgentContext();
19552
+ const client = ctx.createApiClient(agentContext);
19553
+ const res = await client.request(
19554
+ "GET",
19555
+ `/internal/agent/${encodeURIComponent(agentContext.agentId)}/mention-actions/pending`
19556
+ );
19557
+ if (!res.ok || !res.data) {
19558
+ throw cliError(res.status >= 500 ? "SERVER_5XX" : "MENTION_PENDING_FAILED", res.error ?? `HTTP ${res.status}`);
19559
+ }
19560
+ const actions = normalizePendingMentionActions(res.data);
19561
+ if (opts.json) {
19562
+ writeJson(ctx.io, { ok: true, pendingMentionActions: actions });
19563
+ return;
19564
+ }
19565
+ writeText(ctx.io, formatPendingMentionActions(actions, { source: "pending" }));
19566
+ }
19567
+ );
19568
+ function registerMentionPendingCommand(parent, runtimeOptions = {}) {
19569
+ registerCliCommand(parent, mentionPendingCommand, runtimeOptions);
19570
+ }
19571
+
19572
+ // src/commands/mention/index.ts
19573
+ function registerMentionCommands(parent, runtimeOptions = {}) {
19574
+ registerMentionPendingCommand(parent, runtimeOptions);
19575
+ registerMentionExecuteCommands(parent, runtimeOptions);
19576
+ }
19577
+
19177
19578
  // src/commands/profile/_format.ts
19178
19579
  function formatCreatedAgents(createdAgents) {
19179
19580
  if (createdAgents.length === 0) {
@@ -20489,6 +20890,8 @@ registerTaskCreateCommand(taskCmd);
20489
20890
  registerTaskClaimCommand(taskCmd);
20490
20891
  registerTaskUnclaimCommand(taskCmd);
20491
20892
  registerTaskUpdateCommand(taskCmd);
20893
+ var mentionCmd = program.command("mention").description("Sender-side mention action operations");
20894
+ registerMentionCommands(mentionCmd);
20492
20895
  var profileCmd = program.command("profile").description("Profile operations");
20493
20896
  registerProfileShowCommand(profileCmd);
20494
20897
  registerProfileUpdateCommand(profileCmd);
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@botiverse/raft",
3
- "version": "0.0.12",
3
+ "version": "0.0.14",
4
4
  "type": "module"
5
5
  }
package/dist/core.js CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  runBundledSlockCli,
12
12
  scanWorkspaceDirectories,
13
13
  subscribeDaemonLogs
14
- } from "./chunk-XA4ZPIWE.js";
14
+ } from "./chunk-FTEZQN4P.js";
15
15
  export {
16
16
  DAEMON_CLI_USAGE,
17
17
  DaemonCore,
@@ -175,6 +175,7 @@ function emitCliTransportNormalizedError(attrs, env = process.env) {
175
175
  function routeFamilyForPath(pathname) {
176
176
  const normalized = pathname.split("?")[0] || "/";
177
177
  if (normalized === "/internal/agent-api/send") return "agent-api/send";
178
+ if (normalized === "/internal/agent-api/activity") return "agent-api/activity";
178
179
  if (normalized === "/internal/agent-api/events") return "agent-api/events";
179
180
  if (normalized === "/internal/agent-api/inbox") return "agent-api/inbox";
180
181
  if (normalized === "/internal/agent-api/receive-ack") return "agent-api/events";
@@ -332,6 +333,9 @@ var ApiClient = class {
332
333
  if (suffix === "/reminders" || suffix.startsWith("/reminders?") || suffix.startsWith("/reminders/")) {
333
334
  return `/internal/agent-api${suffix}`;
334
335
  }
336
+ if (suffix === "/mention-actions/pending" || suffix === "/mention-actions/execute") {
337
+ return `/internal/agent-api${suffix}`;
338
+ }
335
339
  if (suffix === "/receive" || suffix.startsWith("/receive?")) {
336
340
  return "/internal/agent-api/events?since=latest";
337
341
  }
@@ -15526,6 +15530,11 @@ var SERVER_CAPABILITY_MATRIX = {
15526
15530
  joinPublicChannels: true
15527
15531
  })
15528
15532
  };
15533
+ var EXTERNAL_AGENT_ACTIVITY_EVENT_SCHEMA = "raft-activity.v1";
15534
+ var EXTERNAL_AGENT_ACTIVITY_DRAIN_SCHEMA = "raft-activity-drain.v1";
15535
+ var EXTERNAL_AGENT_ACTIVITY_INGEST_SCHEMA = "raft-agent-activity-ingest.v1";
15536
+ var EXTERNAL_AGENT_ACTIVITY_TEXT_LIMIT = 4096;
15537
+ var EXTERNAL_AGENT_ACTIVITY_TOOL_NAME_LIMIT = 120;
15529
15538
  var EXTERNAL_AGENT_RUNTIME_ID = "external";
15530
15539
  var EXTERNAL_AGENT_RUNTIME_MODEL = "external";
15531
15540
  var EXTERNAL_AGENT_RUNTIME_DISPLAY_NAME = "External agent";
@@ -15820,6 +15829,15 @@ async function runAgentCommsBridgeOnce(input) {
15820
15829
  }
15821
15830
  const fetchedLastSeq = typeof fetched.last_seen_hint_seq === "number" ? fetched.last_seen_hint_seq : typeof fetched.last_hint_seq === "number" ? fetched.last_hint_seq : void 0;
15822
15831
  if (typeof fetchedLastSeq === "number") lastSeenHintSeq = Math.max(lastSeenHintSeq ?? 0, fetchedLastSeq);
15832
+ if (input.activitySource && input.activitySink) {
15833
+ output.push(await drainAndForwardActivity({
15834
+ identity,
15835
+ source: input.activitySource,
15836
+ sink: input.activitySink,
15837
+ max: input.activityDrainLimit ?? input.limit ?? 50,
15838
+ now
15839
+ }));
15840
+ }
15823
15841
  store.writeSession({
15824
15842
  coreSessionId,
15825
15843
  ...typeof lastSeenHintSeq === "number" ? { lastSeenHintSeq } : {}
@@ -15827,6 +15845,126 @@ async function runAgentCommsBridgeOnce(input) {
15827
15845
  output.push(lifecycle(identity, processedHintCount > 0 ? "handoff_pending" : "listening_idle", now));
15828
15846
  return output;
15829
15847
  }
15848
+ async function drainAndForwardActivity(input) {
15849
+ let drained;
15850
+ try {
15851
+ drained = await input.source.drainActivity({ max: input.max });
15852
+ } catch (err) {
15853
+ return activityDrainEvent(input.identity, input.now, {
15854
+ outcome: "failed",
15855
+ drainedCount: 0,
15856
+ forwardedCount: 0,
15857
+ rejectedCount: 0,
15858
+ droppedCount: 0,
15859
+ errorClass: err instanceof Error ? err.name : typeof err,
15860
+ errorMessage: errorMessage2(err)
15861
+ });
15862
+ }
15863
+ if (drained.schema !== EXTERNAL_AGENT_ACTIVITY_DRAIN_SCHEMA || !Array.isArray(drained.events)) {
15864
+ return activityDrainEvent(input.identity, input.now, {
15865
+ outcome: "failed",
15866
+ drainedCount: 0,
15867
+ forwardedCount: 0,
15868
+ rejectedCount: 0,
15869
+ droppedCount: normalizeNonNegativeInteger(drained.dropped),
15870
+ errorClass: "ProtocolError",
15871
+ errorMessage: "activity drain response did not match raft-activity-drain.v1"
15872
+ });
15873
+ }
15874
+ const sanitized = sanitizeExternalAgentActivityEvents(drained.events, input.now);
15875
+ if (sanitized.events.length === 0) {
15876
+ return activityDrainEvent(input.identity, input.now, {
15877
+ outcome: "no_events",
15878
+ drainedCount: drained.events.length,
15879
+ forwardedCount: 0,
15880
+ rejectedCount: sanitized.rejectedCount,
15881
+ droppedCount: normalizeNonNegativeInteger(drained.dropped)
15882
+ });
15883
+ }
15884
+ try {
15885
+ await input.sink.forwardActivity({
15886
+ schema: EXTERNAL_AGENT_ACTIVITY_INGEST_SCHEMA,
15887
+ coreSessionId: input.identity.coreSessionId,
15888
+ adapterInstance: input.identity.adapterInstance,
15889
+ events: sanitized.events,
15890
+ dropped: normalizeNonNegativeInteger(drained.dropped)
15891
+ });
15892
+ } catch (err) {
15893
+ return activityDrainEvent(input.identity, input.now, {
15894
+ outcome: "failed",
15895
+ drainedCount: drained.events.length,
15896
+ forwardedCount: 0,
15897
+ rejectedCount: sanitized.rejectedCount,
15898
+ droppedCount: normalizeNonNegativeInteger(drained.dropped),
15899
+ errorClass: err instanceof Error ? err.name : typeof err,
15900
+ errorMessage: errorMessage2(err)
15901
+ });
15902
+ }
15903
+ return activityDrainEvent(input.identity, input.now, {
15904
+ outcome: "forwarded",
15905
+ drainedCount: drained.events.length,
15906
+ forwardedCount: sanitized.events.length,
15907
+ rejectedCount: sanitized.rejectedCount,
15908
+ droppedCount: normalizeNonNegativeInteger(drained.dropped)
15909
+ });
15910
+ }
15911
+ function sanitizeExternalAgentActivityEvents(rawEvents, now = () => /* @__PURE__ */ new Date()) {
15912
+ const events = [];
15913
+ let rejectedCount = 0;
15914
+ for (const raw of rawEvents) {
15915
+ const hookEventName = stringField(raw.hookEventName ?? raw.hook_event_name, 80);
15916
+ if (!hookEventName) {
15917
+ rejectedCount += 1;
15918
+ continue;
15919
+ }
15920
+ const eventId = stringField(raw.eventId ?? raw.event_id, 160) ?? `event_${randomUUID()}`;
15921
+ const sessionId = stringField(raw.sessionId ?? raw.session_id, 200);
15922
+ const toolName = stringField(raw.toolName ?? raw.tool_name, EXTERNAL_AGENT_ACTIVITY_TOOL_NAME_LIMIT);
15923
+ const status = stringField(raw.status, 40);
15924
+ const errorClass = stringField(raw.errorClass ?? raw.error_class, 120);
15925
+ const occurredAt = validIsoDate(raw.occurredAt ?? raw.occurred_at) ?? now().toISOString();
15926
+ const durationMs = normalizeNonNegativeInteger(raw.durationMs ?? raw.duration_ms);
15927
+ const toolInput = truncateActivityText(
15928
+ raw.toolInput ?? raw.tool_input,
15929
+ raw.toolInputTruncated ?? raw.tool_input_truncated ?? raw.truncated
15930
+ );
15931
+ const toolOutput = truncateActivityText(
15932
+ raw.toolOutput ?? raw.tool_output,
15933
+ raw.toolOutputTruncated ?? raw.tool_output_truncated ?? raw.truncated
15934
+ );
15935
+ events.push({
15936
+ schema: EXTERNAL_AGENT_ACTIVITY_EVENT_SCHEMA,
15937
+ eventId,
15938
+ ...sessionId ? { sessionId } : {},
15939
+ hookEventName,
15940
+ ...toolName ? { toolName } : {},
15941
+ ...status ? { status } : {},
15942
+ occurredAt,
15943
+ ...durationMs > 0 ? { durationMs } : {},
15944
+ ...errorClass ? { errorClass } : {},
15945
+ ...toolInput.text !== void 0 ? { toolInput: toolInput.text, toolInputTruncated: toolInput.truncated } : {},
15946
+ ...toolOutput.text !== void 0 ? { toolOutput: toolOutput.text, toolOutputTruncated: toolOutput.truncated } : {}
15947
+ });
15948
+ }
15949
+ return { events, rejectedCount };
15950
+ }
15951
+ function activityDrainEvent(identity, now, result2) {
15952
+ return {
15953
+ type: "agent_comms.activity_drain",
15954
+ eventId: `event_${randomUUID()}`,
15955
+ protocolVersion: AGENT_COMMS_PROTOCOL_VERSION,
15956
+ timestamp: now().toISOString(),
15957
+ coreSessionId: identity.coreSessionId,
15958
+ agentId: identity.agentId,
15959
+ profile: identity.profileSlug,
15960
+ profileSlug: identity.profileSlug,
15961
+ adapterInstance: identity.adapterInstance,
15962
+ runtimeSession: null,
15963
+ source: "slock-agent-bridge",
15964
+ provenance: commsCoreProvenance("comms_core"),
15965
+ ...result2
15966
+ };
15967
+ }
15830
15968
  var BRIDGE_LOG_MAX_BYTES = 5 * 1024 * 1024;
15831
15969
  function rotateBridgeLogIfNeeded(logFile) {
15832
15970
  try {
@@ -16156,6 +16294,46 @@ function stripContentFields(wakeHint) {
16156
16294
  delete clone2.message;
16157
16295
  return clone2;
16158
16296
  }
16297
+ function stringField(value, maxLength) {
16298
+ if (typeof value !== "string") return void 0;
16299
+ const trimmed = value.replace(/\s+/g, " ").trim();
16300
+ if (!trimmed) return void 0;
16301
+ return trimmed.slice(0, maxLength);
16302
+ }
16303
+ function validIsoDate(value) {
16304
+ if (typeof value !== "string") return void 0;
16305
+ const parsed = Date.parse(value);
16306
+ if (Number.isNaN(parsed)) return void 0;
16307
+ return new Date(parsed).toISOString();
16308
+ }
16309
+ function normalizeNonNegativeInteger(value) {
16310
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return 0;
16311
+ return Math.floor(value);
16312
+ }
16313
+ function truncateActivityText(value, alreadyTruncated) {
16314
+ if (value === void 0 || value === null) return { truncated: Boolean(alreadyTruncated) };
16315
+ let text;
16316
+ if (typeof value === "string") {
16317
+ text = value;
16318
+ } else {
16319
+ try {
16320
+ text = JSON.stringify(value);
16321
+ } catch {
16322
+ text = String(value);
16323
+ }
16324
+ }
16325
+ const marker = "\n[truncated]";
16326
+ if (text.length > EXTERNAL_AGENT_ACTIVITY_TEXT_LIMIT) {
16327
+ return {
16328
+ text: `${text.slice(0, Math.max(0, EXTERNAL_AGENT_ACTIVITY_TEXT_LIMIT - marker.length))}${marker}`,
16329
+ truncated: true
16330
+ };
16331
+ }
16332
+ return { text, truncated: Boolean(alreadyTruncated) };
16333
+ }
16334
+ function errorMessage2(err) {
16335
+ return (err instanceof Error ? err.message : String(err)).slice(0, 200);
16336
+ }
16159
16337
  function safePathSegment(value) {
16160
16338
  return value.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 120) || "unknown";
16161
16339
  }
@@ -16309,7 +16487,10 @@ var agentBridgeCommand = defineCommand(
16309
16487
  { flags: "--wake-channel-endpoint <url>", description: "Localhost wake endpoint exposed by the runtime's Raft channel plugin (see docs/wake-endpoint-contract.md in raft-external-agents)." },
16310
16488
  { flags: "--wake-channel-token <token>", description: "Optional shared token for the Raft channel wake endpoint (env: RAFT_CHANNEL_TOKEN)." },
16311
16489
  { flags: "--runtime-session <id>", description: "Optional runtime session id when the adapter endpoint does not return one." },
16312
- { flags: "--reconcile-interval-ms <ms>", description: "Re-peek the full server-pending set every <ms> and re-wake hints that were injected but never consumed (0 disables; default 120000)." }
16490
+ { flags: "--reconcile-interval-ms <ms>", description: "Re-peek the full server-pending set every <ms> and re-wake hints that were injected but never consumed (0 disables; default 120000)." },
16491
+ { flags: "--activity-channel-endpoint <url>", description: "Localhost activity drain endpoint exposed by the runtime's Raft channel plugin. Defaults to /activity/drain derived from --wake-channel-endpoint." },
16492
+ { flags: "--activity-channel-token <token>", description: "Optional shared token for the activity drain endpoint (env: RAFT_CHANNEL_TOKEN)." },
16493
+ { flags: "--activity-drain-limit <n>", description: "Maximum plugin activity events to drain per bridge iteration." }
16313
16494
  ]
16314
16495
  },
16315
16496
  async (ctx, options) => {
@@ -16324,10 +16505,13 @@ var agentBridgeCommand = defineCommand(
16324
16505
  const pollIntervalMs = parsePositiveInt(options.pollIntervalMs, 5e3, "poll-interval-ms");
16325
16506
  const reconcileIntervalMs = options.reconcileIntervalMs === "0" ? 0 : parsePositiveInt(options.reconcileIntervalMs, 12e4, "reconcile-interval-ms");
16326
16507
  const limit = parsePositiveInt(options.limit, 50, "limit");
16508
+ const activityDrainLimit = parsePositiveInt(options.activityDrainLimit, 50, "activity-drain-limit");
16327
16509
  const client = ctx.createApiClient(agentContext);
16328
16510
  const pollSource = createAgentApiWakeHintSource(client);
16329
16511
  const streamSource = options.once ? null : createAgentApiWakeHintStreamSource(client, ctx.env);
16330
16512
  const wakeAdapter = createWakeAdapter(options, ctx.env);
16513
+ const activitySource = createActivityDrainSource(options, ctx.env);
16514
+ const activitySink = activitySource ? createAgentApiActivitySink(client) : void 0;
16331
16515
  let bridgeLock;
16332
16516
  try {
16333
16517
  bridgeLock = acquireAgentCommsBridgeLock({
@@ -16381,6 +16565,9 @@ var agentBridgeCommand = defineCommand(
16381
16565
  replayPending,
16382
16566
  wakeAdapter,
16383
16567
  runtimeSession: options.runtimeSession ?? null,
16568
+ activitySource,
16569
+ activitySink,
16570
+ activityDrainLimit,
16384
16571
  recentInjections
16385
16572
  });
16386
16573
  for (const event of events) emit(event);
@@ -16663,6 +16850,59 @@ function createWakeAdapter(options, env = process.env) {
16663
16850
  token: options.wakeChannelToken ?? env.RAFT_CHANNEL_TOKEN
16664
16851
  });
16665
16852
  }
16853
+ function createActivityDrainSource(options, env = process.env) {
16854
+ const endpointUrl = options.activityChannelEndpoint ?? deriveActivityDrainEndpoint(options.wakeChannelEndpoint);
16855
+ if (!endpointUrl) return void 0;
16856
+ const token = options.activityChannelToken ?? options.wakeChannelToken ?? env.RAFT_CHANNEL_TOKEN;
16857
+ return {
16858
+ async drainActivity(input) {
16859
+ const url2 = new URL(endpointUrl);
16860
+ url2.searchParams.set("max", String(input.max));
16861
+ const signal = typeof AbortSignal.timeout === "function" ? AbortSignal.timeout(3e3) : void 0;
16862
+ const response = await fetch(url2, {
16863
+ method: "GET",
16864
+ signal,
16865
+ headers: {
16866
+ ...token ? { "x-raft-bridge-token": token } : {}
16867
+ }
16868
+ });
16869
+ const contentType = response.headers.get("content-type") ?? "";
16870
+ const data = contentType.includes("application/json") ? await response.json() : {};
16871
+ if (!response.ok) {
16872
+ throw new CliError({
16873
+ code: response.status >= 500 ? "SERVER_5XX" : "BRIDGE_ACTIVITY_DRAIN_FAILED",
16874
+ message: typeof data?.error === "string" ? data.error : `HTTP ${response.status}`
16875
+ });
16876
+ }
16877
+ return data;
16878
+ }
16879
+ };
16880
+ }
16881
+ function createAgentApiActivitySink(client) {
16882
+ return {
16883
+ async forwardActivity(input) {
16884
+ const response = await client.request("POST", "/internal/agent-api/activity", input);
16885
+ if (!response.ok) {
16886
+ throw new CliError({
16887
+ code: response.status >= 500 ? "SERVER_5XX" : "BRIDGE_ACTIVITY_FORWARD_FAILED",
16888
+ message: response.error ?? `HTTP ${response.status}`
16889
+ });
16890
+ }
16891
+ }
16892
+ };
16893
+ }
16894
+ function deriveActivityDrainEndpoint(wakeChannelEndpoint) {
16895
+ if (!wakeChannelEndpoint) return void 0;
16896
+ try {
16897
+ const url2 = new URL(wakeChannelEndpoint);
16898
+ url2.pathname = "/activity/drain";
16899
+ url2.search = "";
16900
+ url2.hash = "";
16901
+ return url2.toString();
16902
+ } catch {
16903
+ return void 0;
16904
+ }
16905
+ }
16666
16906
  function sleep(ms) {
16667
16907
  return new Promise((resolve) => setTimeout(resolve, ms));
16668
16908
  }
@@ -17548,6 +17788,84 @@ ${opts.heldAction} Review the bounded context shown here, then choose one path.$
17548
17788
 
17549
17789
  ` + (opts.draftInstructions ?? "") + continueAnyway;
17550
17790
  }
17791
+ function normalizeAction(action) {
17792
+ if (action === "notify" || action === "notify_only") return "notify";
17793
+ if (action === "invite" || action === "add") return "invite";
17794
+ return null;
17795
+ }
17796
+ function formatActionCommands(action) {
17797
+ const verbs = action.availableActions.map(normalizeAction).filter((verb) => verb !== null);
17798
+ return Array.from(new Set(verbs)).map((verb) => ` ${verb}: slock mention ${verb} ${action.resolutionId}`);
17799
+ }
17800
+ function normalizePendingMentionActions(data) {
17801
+ const value = data;
17802
+ const raw = Array.isArray(value?.pendingMentionActions) ? value.pendingMentionActions : Array.isArray(value?.actions) ? value.actions : Array.isArray(value?.results) ? value.results : [];
17803
+ return raw.filter((item) => Boolean(item && typeof item === "object")).map((item) => ({
17804
+ resolutionId: String(item.resolutionId ?? item.id ?? ""),
17805
+ messageId: String(item.messageId ?? ""),
17806
+ targetType: String(item.targetType ?? "unknown"),
17807
+ targetHandle: String(item.targetHandle ?? ""),
17808
+ reason: String(item.reason ?? "Mention target was not notified at send time."),
17809
+ availableActions: Array.isArray(item.availableActions) ? item.availableActions.map(String) : [],
17810
+ expiresAt: typeof item.expiresAt === "string" ? item.expiresAt : null
17811
+ })).filter((item) => item.resolutionId.length > 0);
17812
+ }
17813
+ function normalizeMentionActionResults(data) {
17814
+ const value = data;
17815
+ const raw = Array.isArray(value?.results) ? value.results : Array.isArray(value?.actionResults) ? value.actionResults : [];
17816
+ return raw.filter((item) => Boolean(item && typeof item === "object")).map((item) => ({
17817
+ resolutionId: String(item.resolutionId ?? item.id ?? ""),
17818
+ status: String(item.status ?? "not_found"),
17819
+ action: typeof item.action === "string" ? normalizeAction(item.action) : null,
17820
+ messageId: typeof item.messageId === "string" ? item.messageId : null,
17821
+ channelId: typeof item.channelId === "string" ? item.channelId : null,
17822
+ targetType: typeof item.targetType === "string" ? item.targetType : null,
17823
+ targetId: typeof item.targetId === "string" ? item.targetId : null,
17824
+ targetHandle: typeof item.targetHandle === "string" ? item.targetHandle : null,
17825
+ message: typeof item.message === "string" ? item.message : null,
17826
+ reason: typeof item.reason === "string" ? item.reason : null,
17827
+ dedupedResolutionIds: Array.isArray(item.dedupedResolutionIds) ? item.dedupedResolutionIds.filter((id) => typeof id === "string" && id.length > 0) : []
17828
+ })).filter((item) => item.resolutionId.length > 0);
17829
+ }
17830
+ function formatPendingMentionActions(actions, opts = {}) {
17831
+ if (actions.length === 0) {
17832
+ return opts.source === "pending" ? "Pending mention actions\n\nNo pending mention actions.\n" : "";
17833
+ }
17834
+ const lines = ["Pending mention actions", ""];
17835
+ for (const action of actions) {
17836
+ const target = action.targetHandle ? `${action.targetHandle} (${action.targetType})` : action.targetType;
17837
+ lines.push(`- ${action.resolutionId} \u2014 ${target}`);
17838
+ if (action.messageId) lines.push(` message: ${action.messageId}`);
17839
+ lines.push(` reason: ${action.reason}`);
17840
+ if (action.expiresAt) lines.push(` expires: ${action.expiresAt}`);
17841
+ const commands = formatActionCommands(action);
17842
+ if (commands.length > 0) {
17843
+ lines.push(" available commands:");
17844
+ lines.push(...commands);
17845
+ }
17846
+ }
17847
+ return `${lines.join("\n")}
17848
+ `;
17849
+ }
17850
+ function formatMentionActionResults(action, results) {
17851
+ const lines = [`Mention ${action} results`, ""];
17852
+ if (results.length === 0) {
17853
+ lines.push("No result rows returned.");
17854
+ return `${lines.join("\n")}
17855
+ `;
17856
+ }
17857
+ for (const result2 of results) {
17858
+ const target = result2.targetHandle ? ` ${result2.targetHandle}` : "";
17859
+ const detail = result2.message ?? result2.reason;
17860
+ const suffix = detail ? ` \u2014 ${detail}` : "";
17861
+ lines.push(`- ${result2.resolutionId}${target}: ${result2.status}${suffix}`);
17862
+ if (result2.dedupedResolutionIds && result2.dedupedResolutionIds.length > 1) {
17863
+ lines.push(` deduped: ${result2.dedupedResolutionIds.join(", ")}`);
17864
+ }
17865
+ }
17866
+ return `${lines.join("\n")}
17867
+ `;
17868
+ }
17551
17869
  var DEFAULT_LOCAL_DRAFT_TTL_MS = 10 * 60 * 1e3;
17552
17870
  function stateFilePath2(agentId) {
17553
17871
  return path6.join(process.env.SLOCK_CLI_DRAFT_STATE_DIR ?? os3.tmpdir(), "slock-cli-attested-send", agentId, "continue-state.json");
@@ -17850,7 +18168,11 @@ var messageSendCommand = defineCommand(
17850
18168
  --- New messages you may have missed ---
17851
18169
  ${formatMessages(data.recentUnread)}`;
17852
18170
  }
17853
- writeText(ctx.io, `Message sent to ${opts.target}. Message ID: ${data.messageId}${replyHint}${unreadSection}
18171
+ const pendingMentionActions = normalizePendingMentionActions(data);
18172
+ const mentionSection = pendingMentionActions.length > 0 ? `
18173
+
18174
+ ${formatPendingMentionActions(pendingMentionActions, { source: "send" }).trimEnd()}` : "";
18175
+ writeText(ctx.io, `Message sent to ${opts.target}. Message ID: ${data.messageId}${replyHint}${mentionSection}${unreadSection}
17854
18176
  `);
17855
18177
  }
17856
18178
  );
@@ -18906,6 +19228,77 @@ var taskUpdateCommand = defineCommand(
18906
19228
  function registerTaskUpdateCommand(parent, runtimeOptions) {
18907
19229
  registerCliCommand(parent, taskUpdateCommand, runtimeOptions);
18908
19230
  }
19231
+ function normalizeResolutionIds(rawIds) {
19232
+ const ids = (rawIds ?? []).map((id) => id.trim()).filter(Boolean);
19233
+ if (ids.length === 0) {
19234
+ throw cliError("INVALID_ARG", "At least one resolution id is required.");
19235
+ }
19236
+ return ids;
19237
+ }
19238
+ function buildMentionExecuteCommand(action) {
19239
+ return defineCommand(
19240
+ {
19241
+ name: action,
19242
+ description: `${action === "notify" ? "Notify" : "Invite"} unresolved mention targets by resolution id`,
19243
+ arguments: ["<resolutionIds...>"],
19244
+ options: [{ flags: "--json", description: "Emit machine-readable JSON" }]
19245
+ },
19246
+ async (ctx, resolutionIds, opts = {}) => {
19247
+ const ids = normalizeResolutionIds(resolutionIds);
19248
+ const agentContext = ctx.loadAgentContext();
19249
+ const client = ctx.createApiClient(agentContext);
19250
+ const res = await client.request(
19251
+ "POST",
19252
+ `/internal/agent/${encodeURIComponent(agentContext.agentId)}/mention-actions/execute`,
19253
+ { action, resolutionIds: ids }
19254
+ );
19255
+ if (!res.ok || !res.data) {
19256
+ throw cliError(res.status >= 500 ? "SERVER_5XX" : "MENTION_ACTION_FAILED", res.error ?? `HTTP ${res.status}`);
19257
+ }
19258
+ const results = normalizeMentionActionResults(res.data);
19259
+ if (opts.json) {
19260
+ writeJson(ctx.io, { ok: true, action, results });
19261
+ return;
19262
+ }
19263
+ writeText(ctx.io, formatMentionActionResults(action, results));
19264
+ }
19265
+ );
19266
+ }
19267
+ function registerMentionExecuteCommands(parent, runtimeOptions = {}) {
19268
+ registerCliCommand(parent, buildMentionExecuteCommand("notify"), runtimeOptions);
19269
+ registerCliCommand(parent, buildMentionExecuteCommand("invite"), runtimeOptions);
19270
+ }
19271
+ var mentionPendingCommand = defineCommand(
19272
+ {
19273
+ name: "pending",
19274
+ description: "List sender-side pending mention actions",
19275
+ options: [{ flags: "--json", description: "Emit machine-readable JSON" }]
19276
+ },
19277
+ async (ctx, opts = {}) => {
19278
+ const agentContext = ctx.loadAgentContext();
19279
+ const client = ctx.createApiClient(agentContext);
19280
+ const res = await client.request(
19281
+ "GET",
19282
+ `/internal/agent/${encodeURIComponent(agentContext.agentId)}/mention-actions/pending`
19283
+ );
19284
+ if (!res.ok || !res.data) {
19285
+ throw cliError(res.status >= 500 ? "SERVER_5XX" : "MENTION_PENDING_FAILED", res.error ?? `HTTP ${res.status}`);
19286
+ }
19287
+ const actions = normalizePendingMentionActions(res.data);
19288
+ if (opts.json) {
19289
+ writeJson(ctx.io, { ok: true, pendingMentionActions: actions });
19290
+ return;
19291
+ }
19292
+ writeText(ctx.io, formatPendingMentionActions(actions, { source: "pending" }));
19293
+ }
19294
+ );
19295
+ function registerMentionPendingCommand(parent, runtimeOptions = {}) {
19296
+ registerCliCommand(parent, mentionPendingCommand, runtimeOptions);
19297
+ }
19298
+ function registerMentionCommands(parent, runtimeOptions = {}) {
19299
+ registerMentionPendingCommand(parent, runtimeOptions);
19300
+ registerMentionExecuteCommands(parent, runtimeOptions);
19301
+ }
18909
19302
  function formatCreatedAgents(createdAgents) {
18910
19303
  if (createdAgents.length === 0) {
18911
19304
  return ["- Created Agents: none"];
@@ -20181,6 +20574,8 @@ registerTaskCreateCommand(taskCmd);
20181
20574
  registerTaskClaimCommand(taskCmd);
20182
20575
  registerTaskUnclaimCommand(taskCmd);
20183
20576
  registerTaskUpdateCommand(taskCmd);
20577
+ var mentionCmd = program.command("mention").description("Sender-side mention action operations");
20578
+ registerMentionCommands(mentionCmd);
20184
20579
  var profileCmd = program.command("profile").description("Profile operations");
20185
20580
  registerProfileShowCommand(profileCmd);
20186
20581
  registerProfileUpdateCommand(profileCmd);
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  DAEMON_CLI_USAGE,
4
4
  DaemonCore,
5
5
  parseDaemonCliArgs
6
- } from "./chunk-XA4ZPIWE.js";
6
+ } from "./chunk-FTEZQN4P.js";
7
7
 
8
8
  // src/index.ts
9
9
  var parsedArgs = parseDaemonCliArgs(process.argv.slice(2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botiverse/raft-daemon",
3
- "version": "0.58.2",
3
+ "version": "0.59.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "raft-daemon": "dist/raft-daemon.js",