@openacp/cli 2026.410.2 → 2026.413.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{channel-CFMUPzvH.d.ts → channel-Dg1nGCYa.d.ts} +29 -1
- package/dist/cli.js +1324 -135
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +250 -22
- package/dist/index.js +322 -34
- package/dist/index.js.map +1 -1
- package/dist/testing.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -793,6 +793,8 @@ var init_events = __esm({
|
|
|
793
793
|
TURN_START: "turn:start",
|
|
794
794
|
/** Turn ended (always fires, even on error) — read-only, fire-and-forget. */
|
|
795
795
|
TURN_END: "turn:end",
|
|
796
|
+
/** After a turn completes — full assembled agent text, read-only, fire-and-forget. */
|
|
797
|
+
AGENT_AFTER_TURN: "agent:afterTurn",
|
|
796
798
|
// --- Session lifecycle ---
|
|
797
799
|
/** Before a new session is created — modifiable, can block. */
|
|
798
800
|
SESSION_BEFORE_CREATE: "session:beforeCreate",
|
|
@@ -870,7 +872,22 @@ var init_events = __esm({
|
|
|
870
872
|
PLUGIN_UNLOADED: "plugin:unloaded",
|
|
871
873
|
// --- Usage ---
|
|
872
874
|
/** Fired when a token usage record is captured (consumed by usage plugin). */
|
|
873
|
-
USAGE_RECORDED: "usage:recorded"
|
|
875
|
+
USAGE_RECORDED: "usage:recorded",
|
|
876
|
+
// --- Identity lifecycle ---
|
|
877
|
+
/** Fired when a new user+identity record is created. */
|
|
878
|
+
IDENTITY_CREATED: "identity:created",
|
|
879
|
+
/** Fired when user profile fields change. */
|
|
880
|
+
IDENTITY_UPDATED: "identity:updated",
|
|
881
|
+
/** Fired when two identities are linked (same person). */
|
|
882
|
+
IDENTITY_LINKED: "identity:linked",
|
|
883
|
+
/** Fired when an identity is unlinked into a new user. */
|
|
884
|
+
IDENTITY_UNLINKED: "identity:unlinked",
|
|
885
|
+
/** Fired when two user records are merged during a link operation. */
|
|
886
|
+
IDENTITY_USER_MERGED: "identity:userMerged",
|
|
887
|
+
/** Fired when a user's role changes. */
|
|
888
|
+
IDENTITY_ROLE_CHANGED: "identity:roleChanged",
|
|
889
|
+
/** Fired when a user is seen (throttled). */
|
|
890
|
+
IDENTITY_SEEN: "identity:seen"
|
|
874
891
|
};
|
|
875
892
|
SessionEv = {
|
|
876
893
|
/** Agent produced an event (text, tool_call, etc.) during a turn. */
|
|
@@ -2829,14 +2846,20 @@ var init_api_client = __esm({
|
|
|
2829
2846
|
});
|
|
2830
2847
|
|
|
2831
2848
|
// src/plugins/notifications/notification.ts
|
|
2832
|
-
var
|
|
2849
|
+
var NotificationService;
|
|
2833
2850
|
var init_notification = __esm({
|
|
2834
2851
|
"src/plugins/notifications/notification.ts"() {
|
|
2835
2852
|
"use strict";
|
|
2836
|
-
|
|
2853
|
+
NotificationService = class {
|
|
2837
2854
|
constructor(adapters) {
|
|
2838
2855
|
this.adapters = adapters;
|
|
2839
2856
|
}
|
|
2857
|
+
identityResolver;
|
|
2858
|
+
/** Inject identity resolver for user-targeted notifications. */
|
|
2859
|
+
setIdentityResolver(resolver) {
|
|
2860
|
+
this.identityResolver = resolver;
|
|
2861
|
+
}
|
|
2862
|
+
// --- Legacy API (backward compat with NotificationManager) ---
|
|
2840
2863
|
/**
|
|
2841
2864
|
* Send a notification to a specific channel adapter.
|
|
2842
2865
|
*
|
|
@@ -2865,6 +2888,62 @@ var init_notification = __esm({
|
|
|
2865
2888
|
}
|
|
2866
2889
|
}
|
|
2867
2890
|
}
|
|
2891
|
+
// --- New user-targeted API ---
|
|
2892
|
+
/**
|
|
2893
|
+
* Send a notification to a user across all their linked platforms.
|
|
2894
|
+
* Fire-and-forget — never throws, swallows all errors.
|
|
2895
|
+
*/
|
|
2896
|
+
async notifyUser(target, message, options) {
|
|
2897
|
+
try {
|
|
2898
|
+
await this._resolveAndDeliver(target, message, options);
|
|
2899
|
+
} catch {
|
|
2900
|
+
}
|
|
2901
|
+
}
|
|
2902
|
+
async _resolveAndDeliver(target, message, options) {
|
|
2903
|
+
if ("channelId" in target && "platformId" in target) {
|
|
2904
|
+
const adapter = this.adapters.get(target.channelId);
|
|
2905
|
+
if (!adapter?.sendUserNotification) return;
|
|
2906
|
+
await adapter.sendUserNotification(target.platformId, message, {
|
|
2907
|
+
via: options?.via,
|
|
2908
|
+
topicId: options?.topicId,
|
|
2909
|
+
sessionId: options?.sessionId
|
|
2910
|
+
});
|
|
2911
|
+
return;
|
|
2912
|
+
}
|
|
2913
|
+
if (!this.identityResolver) return;
|
|
2914
|
+
let identities = [];
|
|
2915
|
+
if ("identityId" in target) {
|
|
2916
|
+
const identity = await this.identityResolver.getIdentity(target.identityId);
|
|
2917
|
+
if (!identity) return;
|
|
2918
|
+
const user = await this.identityResolver.getUser(identity.userId);
|
|
2919
|
+
if (!user) return;
|
|
2920
|
+
identities = await this.identityResolver.getIdentitiesFor(user.userId);
|
|
2921
|
+
} else if ("userId" in target) {
|
|
2922
|
+
identities = await this.identityResolver.getIdentitiesFor(target.userId);
|
|
2923
|
+
}
|
|
2924
|
+
if (options?.onlyPlatforms) {
|
|
2925
|
+
identities = identities.filter((i) => options.onlyPlatforms.includes(i.source));
|
|
2926
|
+
}
|
|
2927
|
+
if (options?.excludePlatforms) {
|
|
2928
|
+
identities = identities.filter((i) => !options.excludePlatforms.includes(i.source));
|
|
2929
|
+
}
|
|
2930
|
+
for (const identity of identities) {
|
|
2931
|
+
const adapter = this.adapters.get(identity.source);
|
|
2932
|
+
if (!adapter?.sendUserNotification) continue;
|
|
2933
|
+
try {
|
|
2934
|
+
await adapter.sendUserNotification(identity.platformId, message, {
|
|
2935
|
+
via: options?.via,
|
|
2936
|
+
topicId: options?.topicId,
|
|
2937
|
+
sessionId: options?.sessionId,
|
|
2938
|
+
platformMention: {
|
|
2939
|
+
platformUsername: identity.platformUsername,
|
|
2940
|
+
platformId: identity.platformId
|
|
2941
|
+
}
|
|
2942
|
+
});
|
|
2943
|
+
} catch {
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2868
2947
|
};
|
|
2869
2948
|
}
|
|
2870
2949
|
});
|
|
@@ -3356,9 +3435,16 @@ async function createApiServer(options) {
|
|
|
3356
3435
|
});
|
|
3357
3436
|
const authPreHandler = createAuthPreHandler(options.getSecret, options.getJwtSecret, options.tokenStore);
|
|
3358
3437
|
app.decorateRequest("auth", null, []);
|
|
3438
|
+
let booted = false;
|
|
3439
|
+
app.addHook("onReady", async () => {
|
|
3440
|
+
booted = true;
|
|
3441
|
+
});
|
|
3359
3442
|
return {
|
|
3360
3443
|
app,
|
|
3361
3444
|
registerPlugin(prefix, plugin, opts) {
|
|
3445
|
+
if (booted) {
|
|
3446
|
+
return;
|
|
3447
|
+
}
|
|
3362
3448
|
const wrappedPlugin = async (pluginApp, pluginOpts) => {
|
|
3363
3449
|
if (opts?.auth !== false) {
|
|
3364
3450
|
pluginApp.addHook("onRequest", authPreHandler);
|
|
@@ -10148,10 +10234,19 @@ var init_adapter = __esm({
|
|
|
10148
10234
|
});
|
|
10149
10235
|
} catch {
|
|
10150
10236
|
}
|
|
10151
|
-
} else if (response.type === "text" || response.type === "error") {
|
|
10152
|
-
|
|
10237
|
+
} else if (response.type === "text" || response.type === "error" || response.type === "adaptive") {
|
|
10238
|
+
let text3;
|
|
10239
|
+
let parseMode;
|
|
10240
|
+
if (response.type === "adaptive") {
|
|
10241
|
+
const variant = response.variants?.["telegram"];
|
|
10242
|
+
text3 = variant?.text ?? response.fallback;
|
|
10243
|
+
parseMode = variant?.parse_mode;
|
|
10244
|
+
} else {
|
|
10245
|
+
text3 = response.type === "text" ? response.text : `\u274C ${response.message}`;
|
|
10246
|
+
parseMode = "Markdown";
|
|
10247
|
+
}
|
|
10153
10248
|
try {
|
|
10154
|
-
await ctx.editMessageText(text3, { parse_mode:
|
|
10249
|
+
await ctx.editMessageText(text3, { ...parseMode && { parse_mode: parseMode } });
|
|
10155
10250
|
} catch {
|
|
10156
10251
|
}
|
|
10157
10252
|
}
|
|
@@ -10433,6 +10528,15 @@ OpenACP will automatically retry until this is resolved.`;
|
|
|
10433
10528
|
message_thread_id: topicId
|
|
10434
10529
|
});
|
|
10435
10530
|
break;
|
|
10531
|
+
case "adaptive": {
|
|
10532
|
+
const variant = response.variants?.["telegram"];
|
|
10533
|
+
const text3 = variant?.text ?? response.fallback;
|
|
10534
|
+
await this.bot.api.sendMessage(chatId, text3, {
|
|
10535
|
+
message_thread_id: topicId,
|
|
10536
|
+
...variant?.parse_mode && { parse_mode: variant.parse_mode }
|
|
10537
|
+
});
|
|
10538
|
+
break;
|
|
10539
|
+
}
|
|
10436
10540
|
case "error":
|
|
10437
10541
|
await this.bot.api.sendMessage(
|
|
10438
10542
|
chatId,
|
|
@@ -10541,12 +10645,25 @@ ${lines.join("\n")}`;
|
|
|
10541
10645
|
}
|
|
10542
10646
|
ctx.replyWithChatAction("typing").catch(() => {
|
|
10543
10647
|
});
|
|
10544
|
-
|
|
10545
|
-
|
|
10546
|
-
|
|
10547
|
-
|
|
10548
|
-
|
|
10549
|
-
|
|
10648
|
+
const fromName = [ctx.from.first_name, ctx.from.last_name].filter(Boolean).join(" ") || void 0;
|
|
10649
|
+
this.core.handleMessage(
|
|
10650
|
+
{
|
|
10651
|
+
channelId: "telegram",
|
|
10652
|
+
threadId: String(threadId),
|
|
10653
|
+
userId: String(ctx.from.id),
|
|
10654
|
+
text: forwardText
|
|
10655
|
+
},
|
|
10656
|
+
// Inject structured channel user info into TurnMeta so plugins can identify
|
|
10657
|
+
// the sender by name without adapter-specific fields on IncomingMessage.
|
|
10658
|
+
{
|
|
10659
|
+
channelUser: {
|
|
10660
|
+
channelId: "telegram",
|
|
10661
|
+
userId: String(ctx.from.id),
|
|
10662
|
+
displayName: fromName,
|
|
10663
|
+
username: ctx.from.username
|
|
10664
|
+
}
|
|
10665
|
+
}
|
|
10666
|
+
).catch((err) => log36.error({ err }, "handleMessage error"));
|
|
10550
10667
|
});
|
|
10551
10668
|
this.bot.on("message:photo", async (ctx) => {
|
|
10552
10669
|
const threadId = ctx.message.message_thread_id;
|
|
@@ -11259,6 +11376,8 @@ var ChannelAdapter = class {
|
|
|
11259
11376
|
}
|
|
11260
11377
|
async archiveSessionTopic(_sessionId) {
|
|
11261
11378
|
}
|
|
11379
|
+
async sendUserNotification(_platformId, _message, _options) {
|
|
11380
|
+
}
|
|
11262
11381
|
};
|
|
11263
11382
|
|
|
11264
11383
|
// src/core/utils/streams.ts
|
|
@@ -12714,16 +12833,16 @@ var PromptQueue = class {
|
|
|
12714
12833
|
* immediately. Otherwise, it's buffered and the returned promise resolves
|
|
12715
12834
|
* only after the prompt finishes processing.
|
|
12716
12835
|
*/
|
|
12717
|
-
async enqueue(text3, attachments, routing, turnId) {
|
|
12836
|
+
async enqueue(text3, attachments, routing, turnId, meta) {
|
|
12718
12837
|
if (this.processing) {
|
|
12719
12838
|
return new Promise((resolve7) => {
|
|
12720
|
-
this.queue.push({ text: text3, attachments, routing, turnId, resolve: resolve7 });
|
|
12839
|
+
this.queue.push({ text: text3, attachments, routing, turnId, meta, resolve: resolve7 });
|
|
12721
12840
|
});
|
|
12722
12841
|
}
|
|
12723
|
-
await this.process(text3, attachments, routing, turnId);
|
|
12842
|
+
await this.process(text3, attachments, routing, turnId, meta);
|
|
12724
12843
|
}
|
|
12725
12844
|
/** Run a single prompt through the processor, then drain the next queued item. */
|
|
12726
|
-
async process(text3, attachments, routing, turnId) {
|
|
12845
|
+
async process(text3, attachments, routing, turnId, meta) {
|
|
12727
12846
|
this.processing = true;
|
|
12728
12847
|
this.abortController = new AbortController();
|
|
12729
12848
|
const { signal } = this.abortController;
|
|
@@ -12733,7 +12852,7 @@ var PromptQueue = class {
|
|
|
12733
12852
|
});
|
|
12734
12853
|
try {
|
|
12735
12854
|
await Promise.race([
|
|
12736
|
-
this.processor(text3, attachments, routing, turnId),
|
|
12855
|
+
this.processor(text3, attachments, routing, turnId, meta),
|
|
12737
12856
|
new Promise((_, reject) => {
|
|
12738
12857
|
signal.addEventListener("abort", () => reject(new Error("Prompt aborted")), { once: true });
|
|
12739
12858
|
})
|
|
@@ -12754,7 +12873,7 @@ var PromptQueue = class {
|
|
|
12754
12873
|
drainNext() {
|
|
12755
12874
|
const next = this.queue.shift();
|
|
12756
12875
|
if (next) {
|
|
12757
|
-
this.process(next.text, next.attachments, next.routing, next.turnId).then(next.resolve);
|
|
12876
|
+
this.process(next.text, next.attachments, next.routing, next.turnId, next.meta).then(next.resolve);
|
|
12758
12877
|
}
|
|
12759
12878
|
}
|
|
12760
12879
|
/**
|
|
@@ -12962,7 +13081,7 @@ var Session = class extends TypedEmitter {
|
|
|
12962
13081
|
this.log = createSessionLogger(this.id, moduleLog);
|
|
12963
13082
|
this.log.info({ agentName: this.agentName }, "Session created");
|
|
12964
13083
|
this.queue = new PromptQueue(
|
|
12965
|
-
(text3, attachments, routing, turnId) => this.processPrompt(text3, attachments, routing, turnId),
|
|
13084
|
+
(text3, attachments, routing, turnId, meta) => this.processPrompt(text3, attachments, routing, turnId, meta),
|
|
12966
13085
|
(err) => {
|
|
12967
13086
|
this.log.error({ err }, "Prompt execution failed");
|
|
12968
13087
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -13061,19 +13180,20 @@ var Session = class extends TypedEmitter {
|
|
|
13061
13180
|
* then adds it to the PromptQueue. Returns a turnId that callers can use to correlate
|
|
13062
13181
|
* queued/processing events before the prompt actually runs.
|
|
13063
13182
|
*/
|
|
13064
|
-
async enqueuePrompt(text3, attachments, routing, externalTurnId) {
|
|
13183
|
+
async enqueuePrompt(text3, attachments, routing, externalTurnId, meta) {
|
|
13065
13184
|
const turnId = externalTurnId ?? nanoid2(8);
|
|
13185
|
+
const turnMeta = meta ?? { turnId };
|
|
13066
13186
|
if (this.middlewareChain) {
|
|
13067
|
-
const payload = { text: text3, attachments, sessionId: this.id, sourceAdapterId: routing?.sourceAdapterId };
|
|
13187
|
+
const payload = { text: text3, attachments, sessionId: this.id, sourceAdapterId: routing?.sourceAdapterId, meta: turnMeta };
|
|
13068
13188
|
const result = await this.middlewareChain.execute(Hook.AGENT_BEFORE_PROMPT, payload, async (p) => p);
|
|
13069
13189
|
if (!result) return turnId;
|
|
13070
13190
|
text3 = result.text;
|
|
13071
13191
|
attachments = result.attachments;
|
|
13072
13192
|
}
|
|
13073
|
-
await this.queue.enqueue(text3, attachments, routing, turnId);
|
|
13193
|
+
await this.queue.enqueue(text3, attachments, routing, turnId, turnMeta);
|
|
13074
13194
|
return turnId;
|
|
13075
13195
|
}
|
|
13076
|
-
async processPrompt(text3, attachments, routing, turnId) {
|
|
13196
|
+
async processPrompt(text3, attachments, routing, turnId, meta) {
|
|
13077
13197
|
if (this._status === "finished") return;
|
|
13078
13198
|
this.activeTurnContext = createTurnContext(
|
|
13079
13199
|
routing?.sourceAdapterId ?? this.channelId,
|
|
@@ -13113,6 +13233,13 @@ ${text3}`;
|
|
|
13113
13233
|
if (accumulatorListener) {
|
|
13114
13234
|
this.on(SessionEv.AGENT_EVENT, accumulatorListener);
|
|
13115
13235
|
}
|
|
13236
|
+
const turnTextBuffer = [];
|
|
13237
|
+
const turnTextListener = (event) => {
|
|
13238
|
+
if (event.type === "text" && typeof event.content === "string") {
|
|
13239
|
+
turnTextBuffer.push(event.content);
|
|
13240
|
+
}
|
|
13241
|
+
};
|
|
13242
|
+
this.on(SessionEv.AGENT_EVENT, turnTextListener);
|
|
13116
13243
|
const mw = this.middlewareChain;
|
|
13117
13244
|
const afterEventListener = mw ? (event) => {
|
|
13118
13245
|
mw.execute(Hook.AGENT_AFTER_EVENT, { sessionId: this.id, event, outgoingMessage: { type: "text", text: "" } }, async (e) => e).catch(() => {
|
|
@@ -13122,7 +13249,7 @@ ${text3}`;
|
|
|
13122
13249
|
this.agentInstance.on(SessionEv.AGENT_EVENT, afterEventListener);
|
|
13123
13250
|
}
|
|
13124
13251
|
if (this.middlewareChain) {
|
|
13125
|
-
this.middlewareChain.execute(Hook.TURN_START, { sessionId: this.id, promptText: processed.text, promptNumber: this.promptCount }, async (p) => p).catch(() => {
|
|
13252
|
+
this.middlewareChain.execute(Hook.TURN_START, { sessionId: this.id, promptText: processed.text, promptNumber: this.promptCount, turnId: this.activeTurnContext?.turnId ?? turnId ?? "", meta }, async (p) => p).catch(() => {
|
|
13126
13253
|
});
|
|
13127
13254
|
}
|
|
13128
13255
|
let stopReason = "end_turn";
|
|
@@ -13148,8 +13275,20 @@ ${text3}`;
|
|
|
13148
13275
|
if (afterEventListener) {
|
|
13149
13276
|
this.agentInstance.off(SessionEv.AGENT_EVENT, afterEventListener);
|
|
13150
13277
|
}
|
|
13278
|
+
this.off(SessionEv.AGENT_EVENT, turnTextListener);
|
|
13279
|
+
const finalTurnId = this.activeTurnContext?.turnId ?? turnId ?? "";
|
|
13151
13280
|
if (this.middlewareChain) {
|
|
13152
|
-
this.middlewareChain.execute(Hook.TURN_END, { sessionId: this.id, stopReason, durationMs: Date.now() - promptStart }, async (p) => p).catch(() => {
|
|
13281
|
+
this.middlewareChain.execute(Hook.TURN_END, { sessionId: this.id, stopReason, durationMs: Date.now() - promptStart, turnId: finalTurnId, meta }, async (p) => p).catch(() => {
|
|
13282
|
+
});
|
|
13283
|
+
}
|
|
13284
|
+
if (this.middlewareChain) {
|
|
13285
|
+
this.middlewareChain.execute(Hook.AGENT_AFTER_TURN, {
|
|
13286
|
+
sessionId: this.id,
|
|
13287
|
+
turnId: finalTurnId,
|
|
13288
|
+
fullText: turnTextBuffer.join(""),
|
|
13289
|
+
stopReason,
|
|
13290
|
+
meta
|
|
13291
|
+
}, async (p) => p).catch(() => {
|
|
13153
13292
|
});
|
|
13154
13293
|
}
|
|
13155
13294
|
this.activeTurnContext = null;
|
|
@@ -14631,8 +14770,7 @@ var SessionFactory = class {
|
|
|
14631
14770
|
const payload = {
|
|
14632
14771
|
agentName: params.agentName,
|
|
14633
14772
|
workingDir: params.workingDirectory,
|
|
14634
|
-
userId: "",
|
|
14635
|
-
// userId is not part of SessionCreateParams — resolved upstream
|
|
14773
|
+
userId: params.userId ?? "",
|
|
14636
14774
|
channelId: params.channelId,
|
|
14637
14775
|
threadId: ""
|
|
14638
14776
|
// threadId is assigned after session creation
|
|
@@ -14734,7 +14872,8 @@ var SessionFactory = class {
|
|
|
14734
14872
|
this.eventBus.emit(BusEvent.SESSION_CREATED, {
|
|
14735
14873
|
sessionId: session.id,
|
|
14736
14874
|
agent: session.agentName,
|
|
14737
|
-
status: session.status
|
|
14875
|
+
status: session.status,
|
|
14876
|
+
userId: createParams.userId
|
|
14738
14877
|
});
|
|
14739
14878
|
}
|
|
14740
14879
|
return session;
|
|
@@ -16102,11 +16241,55 @@ var PluginStorageImpl = class {
|
|
|
16102
16241
|
async list() {
|
|
16103
16242
|
return Object.keys(this.readKv());
|
|
16104
16243
|
}
|
|
16244
|
+
async keys(prefix) {
|
|
16245
|
+
const all = Object.keys(this.readKv());
|
|
16246
|
+
return prefix ? all.filter((k) => k.startsWith(prefix)) : all;
|
|
16247
|
+
}
|
|
16248
|
+
async clear() {
|
|
16249
|
+
this.writeChain = this.writeChain.then(() => {
|
|
16250
|
+
this.writeKv({});
|
|
16251
|
+
});
|
|
16252
|
+
return this.writeChain;
|
|
16253
|
+
}
|
|
16105
16254
|
/** Returns the plugin's data directory, creating it lazily on first access. */
|
|
16106
16255
|
getDataDir() {
|
|
16107
16256
|
fs14.mkdirSync(this.dataDir, { recursive: true });
|
|
16108
16257
|
return this.dataDir;
|
|
16109
16258
|
}
|
|
16259
|
+
/**
|
|
16260
|
+
* Creates a namespaced storage instance scoped to a session.
|
|
16261
|
+
* Keys are prefixed with `session:{sessionId}:` to isolate session data
|
|
16262
|
+
* from global plugin storage in the same backing file.
|
|
16263
|
+
*/
|
|
16264
|
+
forSession(sessionId) {
|
|
16265
|
+
const prefix = `session:${sessionId}:`;
|
|
16266
|
+
return {
|
|
16267
|
+
get: (key) => this.get(`${prefix}${key}`),
|
|
16268
|
+
set: (key, value) => this.set(`${prefix}${key}`, value),
|
|
16269
|
+
delete: (key) => this.delete(`${prefix}${key}`),
|
|
16270
|
+
list: async () => {
|
|
16271
|
+
const all = await this.keys(prefix);
|
|
16272
|
+
return all.map((k) => k.slice(prefix.length));
|
|
16273
|
+
},
|
|
16274
|
+
keys: async (p) => {
|
|
16275
|
+
const full = p ? `${prefix}${p}` : prefix;
|
|
16276
|
+
const all = await this.keys(full);
|
|
16277
|
+
return all.map((k) => k.slice(prefix.length));
|
|
16278
|
+
},
|
|
16279
|
+
clear: async () => {
|
|
16280
|
+
this.writeChain = this.writeChain.then(() => {
|
|
16281
|
+
const data = this.readKv();
|
|
16282
|
+
for (const key of Object.keys(data)) {
|
|
16283
|
+
if (key.startsWith(prefix)) delete data[key];
|
|
16284
|
+
}
|
|
16285
|
+
this.writeKv(data);
|
|
16286
|
+
});
|
|
16287
|
+
return this.writeChain;
|
|
16288
|
+
},
|
|
16289
|
+
getDataDir: () => this.getDataDir(),
|
|
16290
|
+
forSession: (nestedId) => this.forSession(`${sessionId}:${nestedId}`)
|
|
16291
|
+
};
|
|
16292
|
+
}
|
|
16110
16293
|
};
|
|
16111
16294
|
|
|
16112
16295
|
// src/core/plugin/plugin-context.ts
|
|
@@ -16170,9 +16353,52 @@ function createPluginContext(opts) {
|
|
|
16170
16353
|
requirePermission(permissions, "storage:read", "storage.list");
|
|
16171
16354
|
return storageImpl.list();
|
|
16172
16355
|
},
|
|
16356
|
+
async keys(prefix) {
|
|
16357
|
+
requirePermission(permissions, "storage:read", "storage.keys");
|
|
16358
|
+
return storageImpl.keys(prefix);
|
|
16359
|
+
},
|
|
16360
|
+
async clear() {
|
|
16361
|
+
requirePermission(permissions, "storage:write", "storage.clear");
|
|
16362
|
+
return storageImpl.clear();
|
|
16363
|
+
},
|
|
16173
16364
|
getDataDir() {
|
|
16174
16365
|
requirePermission(permissions, "storage:read", "storage.getDataDir");
|
|
16175
16366
|
return storageImpl.getDataDir();
|
|
16367
|
+
},
|
|
16368
|
+
forSession(sessionId) {
|
|
16369
|
+
requirePermission(permissions, "storage:read", "storage.forSession");
|
|
16370
|
+
const scoped = storageImpl.forSession(sessionId);
|
|
16371
|
+
return {
|
|
16372
|
+
get: (key) => {
|
|
16373
|
+
requirePermission(permissions, "storage:read", "storage.get");
|
|
16374
|
+
return scoped.get(key);
|
|
16375
|
+
},
|
|
16376
|
+
set: (key, value) => {
|
|
16377
|
+
requirePermission(permissions, "storage:write", "storage.set");
|
|
16378
|
+
return scoped.set(key, value);
|
|
16379
|
+
},
|
|
16380
|
+
delete: (key) => {
|
|
16381
|
+
requirePermission(permissions, "storage:write", "storage.delete");
|
|
16382
|
+
return scoped.delete(key);
|
|
16383
|
+
},
|
|
16384
|
+
list: () => {
|
|
16385
|
+
requirePermission(permissions, "storage:read", "storage.list");
|
|
16386
|
+
return scoped.list();
|
|
16387
|
+
},
|
|
16388
|
+
keys: (prefix) => {
|
|
16389
|
+
requirePermission(permissions, "storage:read", "storage.keys");
|
|
16390
|
+
return scoped.keys(prefix);
|
|
16391
|
+
},
|
|
16392
|
+
clear: () => {
|
|
16393
|
+
requirePermission(permissions, "storage:write", "storage.clear");
|
|
16394
|
+
return scoped.clear();
|
|
16395
|
+
},
|
|
16396
|
+
getDataDir: () => {
|
|
16397
|
+
requirePermission(permissions, "storage:read", "storage.getDataDir");
|
|
16398
|
+
return scoped.getDataDir();
|
|
16399
|
+
},
|
|
16400
|
+
forSession: (nestedId) => storage.forSession(`${sessionId}:${nestedId}`)
|
|
16401
|
+
};
|
|
16176
16402
|
}
|
|
16177
16403
|
};
|
|
16178
16404
|
const ctx = {
|
|
@@ -16223,6 +16449,26 @@ function createPluginContext(opts) {
|
|
|
16223
16449
|
await router.send(_sessionId, _content);
|
|
16224
16450
|
}
|
|
16225
16451
|
},
|
|
16452
|
+
notify(target, message, options) {
|
|
16453
|
+
requirePermission(permissions, "notifications:send", "notify()");
|
|
16454
|
+
const svc = serviceRegistry.get("notifications");
|
|
16455
|
+
if (svc?.notifyUser) {
|
|
16456
|
+
svc.notifyUser(target, message, options).catch(() => {
|
|
16457
|
+
});
|
|
16458
|
+
}
|
|
16459
|
+
},
|
|
16460
|
+
defineHook(_name) {
|
|
16461
|
+
},
|
|
16462
|
+
async emitHook(name, payload) {
|
|
16463
|
+
const qualifiedName = `plugin:${pluginName}:${name}`;
|
|
16464
|
+
return middlewareChain.execute(qualifiedName, payload, (p) => p);
|
|
16465
|
+
},
|
|
16466
|
+
async getSessionInfo(sessionId) {
|
|
16467
|
+
requirePermission(permissions, "sessions:read", "getSessionInfo()");
|
|
16468
|
+
const sessionMgr = serviceRegistry.get("session-info");
|
|
16469
|
+
if (!sessionMgr) return void 0;
|
|
16470
|
+
return sessionMgr.getSessionInfo(sessionId);
|
|
16471
|
+
},
|
|
16226
16472
|
registerMenuItem(item) {
|
|
16227
16473
|
requirePermission(permissions, "commands:register", "registerMenuItem()");
|
|
16228
16474
|
const menuRegistry = serviceRegistry.get("menu-registry");
|
|
@@ -17272,7 +17518,7 @@ var OpenACPCore = class {
|
|
|
17272
17518
|
*
|
|
17273
17519
|
* If no session is found, the user is told to start one with /new.
|
|
17274
17520
|
*/
|
|
17275
|
-
async handleMessage(message) {
|
|
17521
|
+
async handleMessage(message, initialMeta) {
|
|
17276
17522
|
log16.debug(
|
|
17277
17523
|
{
|
|
17278
17524
|
channelId: message.channelId,
|
|
@@ -17281,10 +17527,12 @@ var OpenACPCore = class {
|
|
|
17281
17527
|
},
|
|
17282
17528
|
"Incoming message"
|
|
17283
17529
|
);
|
|
17530
|
+
const turnId = nanoid3(8);
|
|
17531
|
+
const meta = { turnId, ...initialMeta };
|
|
17284
17532
|
if (this.lifecycleManager?.middlewareChain) {
|
|
17285
17533
|
const result = await this.lifecycleManager.middlewareChain.execute(
|
|
17286
17534
|
Hook.MESSAGE_INCOMING,
|
|
17287
|
-
message,
|
|
17535
|
+
{ ...message, meta },
|
|
17288
17536
|
async (msg) => msg
|
|
17289
17537
|
);
|
|
17290
17538
|
if (!result) return;
|
|
@@ -17336,8 +17584,8 @@ ${text3}`;
|
|
|
17336
17584
|
}
|
|
17337
17585
|
const sourceAdapterId = message.routing?.sourceAdapterId ?? message.channelId;
|
|
17338
17586
|
const routing = sourceAdapterId !== message.routing?.sourceAdapterId ? { ...message.routing, sourceAdapterId } : message.routing;
|
|
17587
|
+
const enrichedMeta = message.meta ?? meta;
|
|
17339
17588
|
if (sourceAdapterId && sourceAdapterId !== "sse" && sourceAdapterId !== "api") {
|
|
17340
|
-
const turnId = nanoid3(8);
|
|
17341
17589
|
this.eventBus.emit(BusEvent.MESSAGE_QUEUED, {
|
|
17342
17590
|
sessionId: session.id,
|
|
17343
17591
|
turnId,
|
|
@@ -17347,10 +17595,50 @@ ${text3}`;
|
|
|
17347
17595
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17348
17596
|
queueDepth: session.queueDepth
|
|
17349
17597
|
});
|
|
17350
|
-
await session.enqueuePrompt(text3, message.attachments, routing, turnId);
|
|
17598
|
+
await session.enqueuePrompt(text3, message.attachments, routing, turnId, enrichedMeta);
|
|
17351
17599
|
} else {
|
|
17352
|
-
await session.enqueuePrompt(text3, message.attachments, routing);
|
|
17600
|
+
await session.enqueuePrompt(text3, message.attachments, routing, turnId, enrichedMeta);
|
|
17601
|
+
}
|
|
17602
|
+
}
|
|
17603
|
+
/**
|
|
17604
|
+
* Send a message to a known session, running the full message:incoming → agent:beforePrompt
|
|
17605
|
+
* middleware chain (same as handleMessage) but without the threadId-based session lookup.
|
|
17606
|
+
*
|
|
17607
|
+
* Used by channels that already hold a direct session reference (e.g. SSE adapter), where
|
|
17608
|
+
* looking up by channelId+threadId is unreliable (API sessions may have no threadId).
|
|
17609
|
+
*
|
|
17610
|
+
* @param session The target session — caller is responsible for validating its status.
|
|
17611
|
+
* @param message Sender context and message content.
|
|
17612
|
+
* @param initialMeta Optional adapter-specific context to seed the TurnMeta bag
|
|
17613
|
+
* (e.g. channelUser with display name/username).
|
|
17614
|
+
*/
|
|
17615
|
+
async handleMessageInSession(session, message, initialMeta) {
|
|
17616
|
+
const turnId = nanoid3(8);
|
|
17617
|
+
const meta = { turnId, ...initialMeta };
|
|
17618
|
+
let text3 = message.text;
|
|
17619
|
+
let { attachments } = message;
|
|
17620
|
+
let enrichedMeta = meta;
|
|
17621
|
+
if (this.lifecycleManager?.middlewareChain) {
|
|
17622
|
+
const payload = {
|
|
17623
|
+
channelId: message.channelId,
|
|
17624
|
+
threadId: session.id,
|
|
17625
|
+
userId: message.userId,
|
|
17626
|
+
text: text3,
|
|
17627
|
+
attachments,
|
|
17628
|
+
meta
|
|
17629
|
+
};
|
|
17630
|
+
const result = await this.lifecycleManager.middlewareChain.execute(
|
|
17631
|
+
Hook.MESSAGE_INCOMING,
|
|
17632
|
+
payload,
|
|
17633
|
+
async (p) => p
|
|
17634
|
+
);
|
|
17635
|
+
if (!result) return;
|
|
17636
|
+
text3 = result.text;
|
|
17637
|
+
attachments = result.attachments;
|
|
17638
|
+
enrichedMeta = result.meta ?? meta;
|
|
17353
17639
|
}
|
|
17640
|
+
const routing = { sourceAdapterId: message.channelId };
|
|
17641
|
+
await session.enqueuePrompt(text3, attachments, routing, turnId, enrichedMeta);
|
|
17354
17642
|
}
|
|
17355
17643
|
// --- Unified Session Creation Pipeline ---
|
|
17356
17644
|
/**
|
|
@@ -19792,7 +20080,7 @@ export {
|
|
|
19792
20080
|
MenuRegistry,
|
|
19793
20081
|
MessageTransformer,
|
|
19794
20082
|
MessagingAdapter,
|
|
19795
|
-
NotificationManager,
|
|
20083
|
+
NotificationService as NotificationManager,
|
|
19796
20084
|
OpenACPCore,
|
|
19797
20085
|
OutputModeResolver,
|
|
19798
20086
|
PRODUCT_GUIDE,
|