@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-
|
|
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
|
-
|
|
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);
|
package/dist/cli/package.json
CHANGED
package/dist/core.js
CHANGED
|
@@ -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
|
-
|
|
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