@ganglion/xacpx 0.11.0 → 0.12.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/bridge/bridge-main.js +29 -1
- package/dist/cli.js +109 -17
- package/dist/commands/handlers/session-handler.d.ts +8 -2
- package/dist/commands/router-types.d.ts +4 -1
- package/dist/control/control-event-bus.d.ts +6 -0
- package/dist/control/workspace-fs.d.ts +12 -0
- package/dist/i18n/types.d.ts +3 -0
- package/dist/plugin-api.js +6 -0
- package/dist/transport/types.d.ts +10 -0
- package/dist/weixin/agent/interface.d.ts +5 -0
- package/package.json +1 -1
|
@@ -68,6 +68,10 @@ function encodeBridgePromptPlanEvent(event) {
|
|
|
68
68
|
return `${JSON.stringify(event)}
|
|
69
69
|
`;
|
|
70
70
|
}
|
|
71
|
+
function encodeBridgePromptUsageEvent(event) {
|
|
72
|
+
return `${JSON.stringify(event)}
|
|
73
|
+
`;
|
|
74
|
+
}
|
|
71
75
|
function encodeBridgeSessionProgressEvent(event) {
|
|
72
76
|
return `${JSON.stringify(event)}
|
|
73
77
|
`;
|
|
@@ -424,6 +428,7 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
424
428
|
let onToolEvent;
|
|
425
429
|
let onThought;
|
|
426
430
|
let onPlan;
|
|
431
|
+
let onUsage;
|
|
427
432
|
let rawStream = false;
|
|
428
433
|
if (options === undefined) {
|
|
429
434
|
toolEventMode = "text";
|
|
@@ -435,6 +440,7 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
435
440
|
onToolEvent = options.onToolEvent;
|
|
436
441
|
onThought = options.onThought;
|
|
437
442
|
onPlan = options.onPlan;
|
|
443
|
+
onUsage = options.onUsage;
|
|
438
444
|
rawStream = options.rawStream ?? false;
|
|
439
445
|
toolEventMode = resolveToolEventMode({
|
|
440
446
|
toolEventMode: options.mode,
|
|
@@ -453,6 +459,7 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
453
459
|
onToolEvent,
|
|
454
460
|
onThought,
|
|
455
461
|
onPlan,
|
|
462
|
+
onUsage,
|
|
456
463
|
finalize() {
|
|
457
464
|
if (this.pendingLine.trim().length > 0) {
|
|
458
465
|
parseStreamingChunks(this, this.pendingLine);
|
|
@@ -517,6 +524,13 @@ function parseStreamingChunks(state, line) {
|
|
|
517
524
|
state.onPlan?.(entries);
|
|
518
525
|
return;
|
|
519
526
|
}
|
|
527
|
+
if (update.sessionUpdate === "usage_update") {
|
|
528
|
+
const used = typeof update.used === "number" && Number.isFinite(update.used) ? update.used : undefined;
|
|
529
|
+
const size = typeof update.size === "number" && Number.isFinite(update.size) ? update.size : undefined;
|
|
530
|
+
if (used !== undefined && size !== undefined && size > 0)
|
|
531
|
+
state.onUsage?.({ used, size });
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
520
534
|
const isThoughtChunk = update.sessionUpdate === "agent_thought_chunk" && update.content?.type === "text" && typeof update.content.text === "string";
|
|
521
535
|
if (isThoughtChunk) {
|
|
522
536
|
const chunk2 = update.content.text;
|
|
@@ -1683,6 +1697,9 @@ var init_channel_cli = __esm(() => {
|
|
|
1683
1697
|
channelAdded: (type) => `Channel ${type} added`,
|
|
1684
1698
|
cannotRemoveLastEnabled: "Cannot remove the last enabled channel.",
|
|
1685
1699
|
channelRemoved: (id) => `Channel ${id} removed`,
|
|
1700
|
+
channelCredentialsCleared: (id) => `Removed stored credentials for channel ${id}`,
|
|
1701
|
+
channelCredentialsClearFailed: (id, error) => `Channel ${id} removed, but clearing its stored credentials failed: ${error}`,
|
|
1702
|
+
channelCredentialsKept: (id) => `Kept stored credentials for channel ${id} (--keep-credentials)`,
|
|
1686
1703
|
cannotDisableLastEnabled: "Cannot disable the last enabled channel.",
|
|
1687
1704
|
channelEnabledToggled: (id, enabled) => `Channel ${id} ${enabled ? "enabled" : "disabled"}`,
|
|
1688
1705
|
channelReplyModeSet: (id, mode) => `Channel ${id} default reply mode set to: ${mode}`,
|
|
@@ -2775,6 +2792,9 @@ var init_channel_cli2 = __esm(() => {
|
|
|
2775
2792
|
channelAdded: (type) => `频道 ${type} 已添加`,
|
|
2776
2793
|
cannotRemoveLastEnabled: "不能删除最后一个启用的频道。",
|
|
2777
2794
|
channelRemoved: (id) => `频道 ${id} 已删除`,
|
|
2795
|
+
channelCredentialsCleared: (id) => `已移除频道 ${id} 的存储凭证`,
|
|
2796
|
+
channelCredentialsClearFailed: (id, error) => `频道 ${id} 已删除,但清除其存储凭证失败:${error}`,
|
|
2797
|
+
channelCredentialsKept: (id) => `已保留频道 ${id} 的存储凭证(--keep-credentials)`,
|
|
2778
2798
|
cannotDisableLastEnabled: "不能禁用最后一个启用的频道。",
|
|
2779
2799
|
channelEnabledToggled: (id, enabled) => `频道 ${id} 已${enabled ? "启用" : "禁用"}`,
|
|
2780
2800
|
channelReplyModeSet: (id, mode) => `频道 ${id} 的默认 reply mode 已设置为:${mode}`,
|
|
@@ -3989,7 +4009,8 @@ async function runStreamingPrompt(command, args, onEvent, options = {}) {
|
|
|
3989
4009
|
rawStream,
|
|
3990
4010
|
...onEvent && (toolEventMode === "structured" || toolEventMode === "both") ? { onToolEvent: (toolEvent) => onEvent({ type: "prompt.tool_event", event: toolEvent }) } : {},
|
|
3991
4011
|
...onEvent ? { onThought: (chunk) => onEvent({ type: "prompt.thought", text: chunk }) } : {},
|
|
3992
|
-
...onEvent ? { onPlan: (entries) => onEvent({ type: "prompt.plan", entries }) } : {}
|
|
4012
|
+
...onEvent ? { onPlan: (entries) => onEvent({ type: "prompt.plan", entries }) } : {},
|
|
4013
|
+
...onEvent ? { onUsage: (usage) => onEvent({ type: "prompt.usage", used: usage.used, size: usage.size }) } : {}
|
|
3993
4014
|
});
|
|
3994
4015
|
let lastReplyAt = now();
|
|
3995
4016
|
const flushBuffer = () => {
|
|
@@ -4280,6 +4301,13 @@ class BridgeServer {
|
|
|
4280
4301
|
event: "prompt.plan",
|
|
4281
4302
|
entries: event.entries
|
|
4282
4303
|
}));
|
|
4304
|
+
} else if (event.type === "prompt.usage") {
|
|
4305
|
+
writeLine?.(encodeBridgePromptUsageEvent({
|
|
4306
|
+
id: requestId,
|
|
4307
|
+
event: "prompt.usage",
|
|
4308
|
+
used: event.used,
|
|
4309
|
+
size: event.size
|
|
4310
|
+
}));
|
|
4283
4311
|
}
|
|
4284
4312
|
});
|
|
4285
4313
|
case "resumeAgentSession":
|
package/dist/cli.js
CHANGED
|
@@ -898,6 +898,9 @@ var init_channel_cli = __esm(() => {
|
|
|
898
898
|
channelAdded: (type) => `Channel ${type} added`,
|
|
899
899
|
cannotRemoveLastEnabled: "Cannot remove the last enabled channel.",
|
|
900
900
|
channelRemoved: (id) => `Channel ${id} removed`,
|
|
901
|
+
channelCredentialsCleared: (id) => `Removed stored credentials for channel ${id}`,
|
|
902
|
+
channelCredentialsClearFailed: (id, error) => `Channel ${id} removed, but clearing its stored credentials failed: ${error}`,
|
|
903
|
+
channelCredentialsKept: (id) => `Kept stored credentials for channel ${id} (--keep-credentials)`,
|
|
901
904
|
cannotDisableLastEnabled: "Cannot disable the last enabled channel.",
|
|
902
905
|
channelEnabledToggled: (id, enabled) => `Channel ${id} ${enabled ? "enabled" : "disabled"}`,
|
|
903
906
|
channelReplyModeSet: (id, mode) => `Channel ${id} default reply mode set to: ${mode}`,
|
|
@@ -1990,6 +1993,9 @@ var init_channel_cli2 = __esm(() => {
|
|
|
1990
1993
|
channelAdded: (type) => `频道 ${type} 已添加`,
|
|
1991
1994
|
cannotRemoveLastEnabled: "不能删除最后一个启用的频道。",
|
|
1992
1995
|
channelRemoved: (id) => `频道 ${id} 已删除`,
|
|
1996
|
+
channelCredentialsCleared: (id) => `已移除频道 ${id} 的存储凭证`,
|
|
1997
|
+
channelCredentialsClearFailed: (id, error) => `频道 ${id} 已删除,但清除其存储凭证失败:${error}`,
|
|
1998
|
+
channelCredentialsKept: (id) => `已保留频道 ${id} 的存储凭证(--keep-credentials)`,
|
|
1993
1999
|
cannotDisableLastEnabled: "不能禁用最后一个启用的频道。",
|
|
1994
2000
|
channelEnabledToggled: (id, enabled) => `频道 ${id} 已${enabled ? "启用" : "禁用"}`,
|
|
1995
2001
|
channelReplyModeSet: (id, mode) => `频道 ${id} 的默认 reply mode 已设置为:${mode}`,
|
|
@@ -22355,7 +22361,7 @@ async function handleSessionRemove(context, chatKey, alias) {
|
|
|
22355
22361
|
return { text: lines.join(`
|
|
22356
22362
|
`) };
|
|
22357
22363
|
}
|
|
22358
|
-
async function promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan) {
|
|
22364
|
+
async function promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage) {
|
|
22359
22365
|
const effectiveReplyMode = resolveEffectiveReplyMode(context.config, chatKey, session3.replyMode);
|
|
22360
22366
|
if (!session3.replyMode)
|
|
22361
22367
|
session3.replyMode = effectiveReplyMode;
|
|
@@ -22387,7 +22393,7 @@ async function promptWithSession(context, session3, chatKey, text, reply, replyC
|
|
|
22387
22393
|
const { promptText, taskIds, groupIds, claimHumanReply } = await preparePromptWithFallback(context, session3, chatKey, text, replyContextToken, accountId);
|
|
22388
22394
|
try {
|
|
22389
22395
|
const replyContext = transportReply && context.quota && getChannelIdFromChatKey(chatKey) === "weixin" ? { chatKey, quota: context.quota } : undefined;
|
|
22390
|
-
const result = await context.interaction.promptTransportSession(session3, promptText, transportReply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan, onPlan);
|
|
22396
|
+
const result = await context.interaction.promptTransportSession(session3, promptText, transportReply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage);
|
|
22391
22397
|
if (claimHumanReply) {
|
|
22392
22398
|
try {
|
|
22393
22399
|
await context.orchestration?.claimActiveHumanReply?.(claimHumanReply);
|
|
@@ -22407,23 +22413,23 @@ async function promptWithSession(context, session3, chatKey, text, reply, replyC
|
|
|
22407
22413
|
throw error2;
|
|
22408
22414
|
}
|
|
22409
22415
|
}
|
|
22410
|
-
async function handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan) {
|
|
22416
|
+
async function handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage) {
|
|
22411
22417
|
try {
|
|
22412
|
-
return await promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan);
|
|
22418
|
+
return await promptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
22413
22419
|
} catch (error2) {
|
|
22414
22420
|
const recovered = await context.recovery.tryRecoverMissingSession(session3, error2);
|
|
22415
22421
|
if (recovered) {
|
|
22416
|
-
return await promptWithSession(context, recovered, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan);
|
|
22422
|
+
return await promptWithSession(context, recovered, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
22417
22423
|
}
|
|
22418
22424
|
return context.recovery.renderTransportError(session3, error2);
|
|
22419
22425
|
}
|
|
22420
22426
|
}
|
|
22421
|
-
async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan) {
|
|
22427
|
+
async function handlePrompt(context, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage) {
|
|
22422
22428
|
const session3 = metadata?.boundSessionAlias ? context.sessions.getResolvedSessionByInternalAlias(metadata.boundSessionAlias) : await context.sessions.getCurrentSession(chatKey);
|
|
22423
22429
|
if (!session3) {
|
|
22424
22430
|
return { text: t().session.noCurrent };
|
|
22425
22431
|
}
|
|
22426
|
-
return await handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan);
|
|
22432
|
+
return await handlePromptWithSession(context, session3, chatKey, text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
22427
22433
|
}
|
|
22428
22434
|
function toCoordinatorRouteChatMetadata(metadata) {
|
|
22429
22435
|
if (!metadata) {
|
|
@@ -24682,7 +24688,7 @@ class CommandRouter {
|
|
|
24682
24688
|
this.logger = logger2 ?? createNoopAppLogger();
|
|
24683
24689
|
this.activeTurns = activeTurns;
|
|
24684
24690
|
}
|
|
24685
|
-
async handle(chatKey, input, reply, replyContextToken, accountId, media, metadata, abortSignal, onToolEvent, onThought, perfSpan, onPlan) {
|
|
24691
|
+
async handle(chatKey, input, reply, replyContextToken, accountId, media, metadata, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage) {
|
|
24686
24692
|
const startedAt = Date.now();
|
|
24687
24693
|
let command = parseCommand(input);
|
|
24688
24694
|
if (metadata?.channel === "control" && command.kind !== "prompt") {
|
|
@@ -24842,16 +24848,16 @@ class CommandRouter {
|
|
|
24842
24848
|
...this.sessions.resolveSession(descriptor.alias, descriptor.agent, descriptor.workspace, descriptor.transportSession),
|
|
24843
24849
|
transient: true
|
|
24844
24850
|
};
|
|
24845
|
-
return await handlePromptWithSession(sessionContext, transientSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan);
|
|
24851
|
+
return await handlePromptWithSession(sessionContext, transientSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
24846
24852
|
}
|
|
24847
24853
|
if (metadata?.scheduledSessionAlias) {
|
|
24848
24854
|
const scheduledSession = await this.sessions.getSession(metadata.scheduledSessionAlias);
|
|
24849
24855
|
if (!scheduledSession) {
|
|
24850
24856
|
throw new Error(`session "${metadata.scheduledSessionAlias}" not found for scheduled prompt`);
|
|
24851
24857
|
}
|
|
24852
|
-
return await handlePromptWithSession(sessionContext, scheduledSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan);
|
|
24858
|
+
return await handlePromptWithSession(sessionContext, scheduledSession, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
24853
24859
|
}
|
|
24854
|
-
return await handlePrompt(sessionContext, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan);
|
|
24860
|
+
return await handlePrompt(sessionContext, chatKey, command.text, reply, replyContextToken, accountId, media, abortSignal, onToolEvent, onThought, perfSpan, metadata, onPlan, onUsage);
|
|
24855
24861
|
}
|
|
24856
24862
|
}
|
|
24857
24863
|
});
|
|
@@ -24999,7 +25005,7 @@ class CommandRouter {
|
|
|
24999
25005
|
setModelTransportSession: (session3, modelId) => this.setModelTransportSession(session3, modelId),
|
|
25000
25006
|
getModelTransportSession: (session3) => this.getModelTransportSession(session3),
|
|
25001
25007
|
cancelTransportSession: (session3) => this.cancelTransportSession(session3),
|
|
25002
|
-
promptTransportSession: (session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpanOverride, onPlan) => this.promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpanOverride ?? perfSpan, onPlan)
|
|
25008
|
+
promptTransportSession: (session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpanOverride, onPlan, onUsage) => this.promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpanOverride ?? perfSpan, onPlan, onUsage)
|
|
25003
25009
|
};
|
|
25004
25010
|
}
|
|
25005
25011
|
createSessionRenderRecoveryOps() {
|
|
@@ -25178,7 +25184,7 @@ class CommandRouter {
|
|
|
25178
25184
|
async checkTransportSession(session3) {
|
|
25179
25185
|
return await this.measureTransportCall("has_session", session3, () => this.transport.hasSession(session3));
|
|
25180
25186
|
}
|
|
25181
|
-
async promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan, onPlan) {
|
|
25187
|
+
async promptTransportSession(session3, text, reply, replyContext, media, abortSignal, onToolEvent, onThought, perfSpan, onPlan, onUsage) {
|
|
25182
25188
|
session3.mcpCoordinatorSession ??= stableCoordinatorSession(session3.transportSession);
|
|
25183
25189
|
let done = false;
|
|
25184
25190
|
let abortRequested = false;
|
|
@@ -25236,7 +25242,8 @@ class CommandRouter {
|
|
|
25236
25242
|
...reply ? { onSegment } : {},
|
|
25237
25243
|
...onToolEvent ? { onToolEvent } : {},
|
|
25238
25244
|
...onThought ? { onThought } : {},
|
|
25239
|
-
...onPlan ? { onPlan } : {}
|
|
25245
|
+
...onPlan ? { onPlan } : {},
|
|
25246
|
+
...onUsage ? { onUsage } : {}
|
|
25240
25247
|
}));
|
|
25241
25248
|
} catch (error2) {
|
|
25242
25249
|
localOutcome = isAbortError2(error2) || abortRequested ? "aborted" : "error";
|
|
@@ -25422,7 +25429,7 @@ class ConsoleAgent {
|
|
|
25422
25429
|
...m.fileName ? { fileName: m.fileName } : {}
|
|
25423
25430
|
})) : undefined;
|
|
25424
25431
|
request.perfSpan?.mark("agent.dispatched");
|
|
25425
|
-
return await this.router.handle(request.conversationId, request.text, request.reply, request.replyContextToken, request.accountId, promptMedia, request.metadata, request.abortSignal, request.onToolEvent, request.onThought, request.perfSpan, request.onPlan);
|
|
25432
|
+
return await this.router.handle(request.conversationId, request.text, request.reply, request.replyContextToken, request.accountId, promptMedia, request.metadata, request.abortSignal, request.onToolEvent, request.onThought, request.perfSpan, request.onPlan, request.onUsage);
|
|
25426
25433
|
}
|
|
25427
25434
|
isKnownCommand(text) {
|
|
25428
25435
|
return isKnownXacpxCommandText(text);
|
|
@@ -30478,6 +30485,10 @@ function encodeBridgePromptPlanEvent(event) {
|
|
|
30478
30485
|
return `${JSON.stringify(event)}
|
|
30479
30486
|
`;
|
|
30480
30487
|
}
|
|
30488
|
+
function encodeBridgePromptUsageEvent(event) {
|
|
30489
|
+
return `${JSON.stringify(event)}
|
|
30490
|
+
`;
|
|
30491
|
+
}
|
|
30481
30492
|
function encodeBridgeSessionProgressEvent(event) {
|
|
30482
30493
|
return `${JSON.stringify(event)}
|
|
30483
30494
|
`;
|
|
@@ -30780,6 +30791,12 @@ class AcpxBridgeClient {
|
|
|
30780
30791
|
type: "prompt.plan",
|
|
30781
30792
|
entries: message.entries
|
|
30782
30793
|
});
|
|
30794
|
+
} else if (message.event === "prompt.usage") {
|
|
30795
|
+
pending.onEvent?.({
|
|
30796
|
+
type: "prompt.usage",
|
|
30797
|
+
used: message.used,
|
|
30798
|
+
size: message.size
|
|
30799
|
+
});
|
|
30783
30800
|
} else if (message.event === "session.progress") {
|
|
30784
30801
|
pending.onEvent?.({
|
|
30785
30802
|
type: "session.progress",
|
|
@@ -31239,6 +31256,8 @@ class AcpxBridgeTransport {
|
|
|
31239
31256
|
let thoughtChain = Promise.resolve();
|
|
31240
31257
|
let planError;
|
|
31241
31258
|
let planChain = Promise.resolve();
|
|
31259
|
+
let usageError;
|
|
31260
|
+
let usageChain = Promise.resolve();
|
|
31242
31261
|
let toolEventMode = resolveToolEventMode(options);
|
|
31243
31262
|
if ((toolEventMode === "structured" || toolEventMode === "both") && !options?.onToolEvent) {
|
|
31244
31263
|
toolEventMode = "text";
|
|
@@ -31291,11 +31310,22 @@ class AcpxBridgeTransport {
|
|
|
31291
31310
|
}
|
|
31292
31311
|
return;
|
|
31293
31312
|
}
|
|
31313
|
+
if (event.type === "prompt.usage") {
|
|
31314
|
+
const onUsage = options?.onUsage;
|
|
31315
|
+
if (onUsage) {
|
|
31316
|
+
const usage = { used: event.used, size: event.size };
|
|
31317
|
+
usageChain = usageChain.then(() => onUsage(usage)).catch((error2) => {
|
|
31318
|
+
usageError ??= error2;
|
|
31319
|
+
});
|
|
31320
|
+
}
|
|
31321
|
+
return;
|
|
31322
|
+
}
|
|
31294
31323
|
});
|
|
31295
31324
|
await segmentChain;
|
|
31296
31325
|
await toolEventChain;
|
|
31297
31326
|
await thoughtChain;
|
|
31298
31327
|
await planChain;
|
|
31328
|
+
await usageChain;
|
|
31299
31329
|
if (sink) {
|
|
31300
31330
|
const { overflowCount } = sink.finalize();
|
|
31301
31331
|
await sink.drain({ timeoutMs: 30000 });
|
|
@@ -31316,6 +31346,9 @@ class AcpxBridgeTransport {
|
|
|
31316
31346
|
if (planError) {
|
|
31317
31347
|
throw planError;
|
|
31318
31348
|
}
|
|
31349
|
+
if (usageError) {
|
|
31350
|
+
throw usageError;
|
|
31351
|
+
}
|
|
31319
31352
|
return { text: summary ? `${summary}
|
|
31320
31353
|
|
|
31321
31354
|
${result.text}` : "" };
|
|
@@ -31332,6 +31365,9 @@ ${result.text}` : "" };
|
|
|
31332
31365
|
if (planError) {
|
|
31333
31366
|
throw planError;
|
|
31334
31367
|
}
|
|
31368
|
+
if (usageError) {
|
|
31369
|
+
throw usageError;
|
|
31370
|
+
}
|
|
31335
31371
|
return result;
|
|
31336
31372
|
}
|
|
31337
31373
|
async setMode(session3, modeId) {
|
|
@@ -31537,6 +31573,7 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
31537
31573
|
let onToolEvent;
|
|
31538
31574
|
let onThought;
|
|
31539
31575
|
let onPlan;
|
|
31576
|
+
let onUsage;
|
|
31540
31577
|
let rawStream = false;
|
|
31541
31578
|
if (options === undefined) {
|
|
31542
31579
|
toolEventMode = "text";
|
|
@@ -31548,6 +31585,7 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
31548
31585
|
onToolEvent = options.onToolEvent;
|
|
31549
31586
|
onThought = options.onThought;
|
|
31550
31587
|
onPlan = options.onPlan;
|
|
31588
|
+
onUsage = options.onUsage;
|
|
31551
31589
|
rawStream = options.rawStream ?? false;
|
|
31552
31590
|
toolEventMode = resolveToolEventMode({
|
|
31553
31591
|
toolEventMode: options.mode,
|
|
@@ -31566,6 +31604,7 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
31566
31604
|
onToolEvent,
|
|
31567
31605
|
onThought,
|
|
31568
31606
|
onPlan,
|
|
31607
|
+
onUsage,
|
|
31569
31608
|
finalize() {
|
|
31570
31609
|
if (this.pendingLine.trim().length > 0) {
|
|
31571
31610
|
parseStreamingChunks(this, this.pendingLine);
|
|
@@ -31630,6 +31669,13 @@ function parseStreamingChunks(state, line) {
|
|
|
31630
31669
|
state.onPlan?.(entries);
|
|
31631
31670
|
return;
|
|
31632
31671
|
}
|
|
31672
|
+
if (update.sessionUpdate === "usage_update") {
|
|
31673
|
+
const used = typeof update.used === "number" && Number.isFinite(update.used) ? update.used : undefined;
|
|
31674
|
+
const size = typeof update.size === "number" && Number.isFinite(update.size) ? update.size : undefined;
|
|
31675
|
+
if (used !== undefined && size !== undefined && size > 0)
|
|
31676
|
+
state.onUsage?.({ used, size });
|
|
31677
|
+
return;
|
|
31678
|
+
}
|
|
31633
31679
|
const isThoughtChunk = update.sessionUpdate === "agent_thought_chunk" && update.content?.type === "text" && typeof update.content.text === "string";
|
|
31634
31680
|
if (isThoughtChunk) {
|
|
31635
31681
|
const chunk2 = update.content.text;
|
|
@@ -33259,7 +33305,29 @@ class WorkspaceFs {
|
|
|
33259
33305
|
}
|
|
33260
33306
|
}
|
|
33261
33307
|
const truncated = diff.length > DIFF_CAP;
|
|
33262
|
-
return { workspace: workspace3, files, diff: truncated ? diff.slice(0, DIFF_CAP) : diff, truncated };
|
|
33308
|
+
return { workspace: workspace3, files, diff: truncated ? diff.slice(0, DIFF_CAP) : diff, truncated, ...await this.gitContext(root) };
|
|
33309
|
+
}
|
|
33310
|
+
async gitContext(root) {
|
|
33311
|
+
const run = async (...args) => {
|
|
33312
|
+
try {
|
|
33313
|
+
return (await execFileAsync("git", ["-C", root, ...args], { maxBuffer: GIT_MAX_BUFFER })).stdout.trim();
|
|
33314
|
+
} catch {
|
|
33315
|
+
return null;
|
|
33316
|
+
}
|
|
33317
|
+
};
|
|
33318
|
+
const ctx = {};
|
|
33319
|
+
const head = await run("rev-parse", "--abbrev-ref", "HEAD");
|
|
33320
|
+
if (head === "HEAD")
|
|
33321
|
+
ctx.detached = true;
|
|
33322
|
+
else if (head)
|
|
33323
|
+
ctx.branch = head;
|
|
33324
|
+
const top = await run("rev-parse", "--show-toplevel");
|
|
33325
|
+
if (top) {
|
|
33326
|
+
const gitDir = await run("rev-parse", "--absolute-git-dir");
|
|
33327
|
+
const commonDir = await run("rev-parse", "--path-format=absolute", "--git-common-dir");
|
|
33328
|
+
ctx.worktree = { root: top, linked: !!gitDir && !!commonDir && gitDir !== commonDir };
|
|
33329
|
+
}
|
|
33330
|
+
return ctx;
|
|
33263
33331
|
}
|
|
33264
33332
|
}
|
|
33265
33333
|
var execFileAsync, MAX_ENTRIES = 2000, FILE_READ_CAP, DIFF_CAP, GIT_MAX_BUFFER, SEARCH_MAX_RESULTS = 200, SEARCH_MAX_SCAN = 20000, SEARCH_SKIP_DIRS;
|
|
@@ -33526,6 +33594,15 @@ ${chunk}` : chunk
|
|
|
33526
33594
|
sessionAlias: params.sessionAlias,
|
|
33527
33595
|
entries
|
|
33528
33596
|
});
|
|
33597
|
+
},
|
|
33598
|
+
onUsage: (usage) => {
|
|
33599
|
+
this.deps.events.emit({
|
|
33600
|
+
type: "turn-usage",
|
|
33601
|
+
chatKey: params.chatKey,
|
|
33602
|
+
sessionAlias: params.sessionAlias,
|
|
33603
|
+
used: usage.used,
|
|
33604
|
+
size: usage.size
|
|
33605
|
+
});
|
|
33529
33606
|
}
|
|
33530
33607
|
});
|
|
33531
33608
|
if (response.text) {
|
|
@@ -50830,6 +50907,7 @@ async function removeChannel(type, rawArgs, deps) {
|
|
|
50830
50907
|
deps.print(restartFlags.message);
|
|
50831
50908
|
return 1;
|
|
50832
50909
|
}
|
|
50910
|
+
const keepCredentials = restartFlags.rest.includes("--keep-credentials");
|
|
50833
50911
|
const config4 = await deps.loadConfig();
|
|
50834
50912
|
ensureChannelsArray(config4);
|
|
50835
50913
|
const channel = findChannel(config4.channels, type);
|
|
@@ -50844,6 +50922,16 @@ async function removeChannel(type, rawArgs, deps) {
|
|
|
50844
50922
|
config4.channels = config4.channels.filter((entry) => entry.id !== channel.id);
|
|
50845
50923
|
await deps.saveChannels(config4.channels);
|
|
50846
50924
|
deps.print(t().channelCli.channelRemoved(channel.id));
|
|
50925
|
+
if (keepCredentials) {
|
|
50926
|
+
deps.print(t().channelCli.channelCredentialsKept(channel.id));
|
|
50927
|
+
} else if (deps.clearChannelCredentials) {
|
|
50928
|
+
try {
|
|
50929
|
+
await deps.clearChannelCredentials(channel);
|
|
50930
|
+
deps.print(t().channelCli.channelCredentialsCleared(channel.id));
|
|
50931
|
+
} catch (error2) {
|
|
50932
|
+
deps.print(t().channelCli.channelCredentialsClearFailed(channel.id, error2 instanceof Error ? error2.message : String(error2)));
|
|
50933
|
+
}
|
|
50934
|
+
}
|
|
50847
50935
|
return await maybeRestartAfterMutation(restartFlags.restart, deps);
|
|
50848
50936
|
}
|
|
50849
50937
|
async function setChannelEnabled(type, enabled, rawArgs, deps) {
|
|
@@ -52569,7 +52657,11 @@ async function createChannelCliDeps(input) {
|
|
|
52569
52657
|
return { state: "indeterminate", pid: status.pid, reason: status.reason };
|
|
52570
52658
|
return { state: "stopped" };
|
|
52571
52659
|
},
|
|
52572
|
-
restartDaemon: async () => await restartDaemonCli(controller, input.print)
|
|
52660
|
+
restartDaemon: async () => await restartDaemonCli(controller, input.print),
|
|
52661
|
+
clearChannelCredentials: async (channel) => {
|
|
52662
|
+
const { createMessageChannel: createMessageChannel2 } = await Promise.resolve().then(() => (init_create_channel(), exports_create_channel));
|
|
52663
|
+
createMessageChannel2(channel.type, channel).logout();
|
|
52664
|
+
}
|
|
52573
52665
|
};
|
|
52574
52666
|
return { ...base, ...input.overrides };
|
|
52575
52667
|
}
|
|
@@ -38,5 +38,11 @@ export declare function handleCancel(context: SessionHandlerContext, chatKey: st
|
|
|
38
38
|
export declare function handleSessionReset(context: SessionHandlerContext, chatKey: string): Promise<RouterResponse>;
|
|
39
39
|
export declare function handleSessionTail(context: SessionHandlerContext, chatKey: string, lines?: number): Promise<RouterResponse>;
|
|
40
40
|
export declare function handleSessionRemove(context: SessionHandlerContext, chatKey: string, alias: string): Promise<RouterResponse>;
|
|
41
|
-
export declare function handlePromptWithSession(context: SessionHandlerContext, session: ResolvedSession, chatKey: string, text: string, reply?: (text: string) => Promise<void>, replyContextToken?: string, accountId?: string, media?: PromptMediaInput, abortSignal?: AbortSignal, onToolEvent?: (event: ToolUseEvent) => void | Promise<void>, onThought?: (chunk: string) => void | Promise<void>, perfSpan?: PerfSpan, metadata?: ChatRequestMetadata, onPlan?: (entries: PlanEntry[]) => void | Promise<void
|
|
42
|
-
|
|
41
|
+
export declare function handlePromptWithSession(context: SessionHandlerContext, session: ResolvedSession, chatKey: string, text: string, reply?: (text: string) => Promise<void>, replyContextToken?: string, accountId?: string, media?: PromptMediaInput, abortSignal?: AbortSignal, onToolEvent?: (event: ToolUseEvent) => void | Promise<void>, onThought?: (chunk: string) => void | Promise<void>, perfSpan?: PerfSpan, metadata?: ChatRequestMetadata, onPlan?: (entries: PlanEntry[]) => void | Promise<void>, onUsage?: (usage: {
|
|
42
|
+
used: number;
|
|
43
|
+
size: number;
|
|
44
|
+
}) => void | Promise<void>): Promise<RouterResponse>;
|
|
45
|
+
export declare function handlePrompt(context: SessionHandlerContext, chatKey: string, text: string, reply?: (text: string) => Promise<void>, replyContextToken?: string, accountId?: string, media?: PromptMediaInput, abortSignal?: AbortSignal, onToolEvent?: (event: ToolUseEvent) => void | Promise<void>, onThought?: (chunk: string) => void | Promise<void>, perfSpan?: PerfSpan, metadata?: ChatRequestMetadata, onPlan?: (entries: PlanEntry[]) => void | Promise<void>, onUsage?: (usage: {
|
|
46
|
+
used: number;
|
|
47
|
+
size: number;
|
|
48
|
+
}) => void | Promise<void>): Promise<RouterResponse>;
|
|
@@ -113,7 +113,10 @@ export interface SessionInteractionOps {
|
|
|
113
113
|
cancelled: boolean;
|
|
114
114
|
message: string;
|
|
115
115
|
}>;
|
|
116
|
-
promptTransportSession: (session: import("../transport/types").ResolvedSession, text: string, reply?: (text: string) => Promise<void>, replyContext?: ReplyQuotaContext, media?: PromptMediaInput, abortSignal?: AbortSignal, onToolEvent?: (event: ToolUseEvent) => void | Promise<void>, onThought?: (chunk: string) => void | Promise<void>, perfSpan?: PerfSpan, onPlan?: (entries: PlanEntry[]) => void | Promise<void
|
|
116
|
+
promptTransportSession: (session: import("../transport/types").ResolvedSession, text: string, reply?: (text: string) => Promise<void>, replyContext?: ReplyQuotaContext, media?: PromptMediaInput, abortSignal?: AbortSignal, onToolEvent?: (event: ToolUseEvent) => void | Promise<void>, onThought?: (chunk: string) => void | Promise<void>, perfSpan?: PerfSpan, onPlan?: (entries: PlanEntry[]) => void | Promise<void>, onUsage?: (usage: {
|
|
117
|
+
used: number;
|
|
118
|
+
size: number;
|
|
119
|
+
}) => void | Promise<void>) => Promise<{
|
|
117
120
|
text: string;
|
|
118
121
|
}>;
|
|
119
122
|
}
|
|
@@ -31,6 +31,12 @@ export type ControlEvent = {
|
|
|
31
31
|
chatKey: string;
|
|
32
32
|
sessionAlias: string;
|
|
33
33
|
entries: PlanEntry[];
|
|
34
|
+
} | {
|
|
35
|
+
type: "turn-usage";
|
|
36
|
+
chatKey: string;
|
|
37
|
+
sessionAlias: string;
|
|
38
|
+
used: number;
|
|
39
|
+
size: number;
|
|
34
40
|
} | {
|
|
35
41
|
type: "turn-finished";
|
|
36
42
|
chatKey: string;
|
|
@@ -31,6 +31,15 @@ export interface WorkspaceDiff {
|
|
|
31
31
|
files: DiffFile[];
|
|
32
32
|
diff: string;
|
|
33
33
|
truncated: boolean;
|
|
34
|
+
/** Symbolic branch name (abbrev-ref HEAD); omitted when HEAD is detached. */
|
|
35
|
+
branch?: string;
|
|
36
|
+
/** True when HEAD is detached (no branch). */
|
|
37
|
+
detached?: boolean;
|
|
38
|
+
/** Working-tree context: its top-level root, and whether it's a linked (non-primary) worktree. */
|
|
39
|
+
worktree?: {
|
|
40
|
+
root: string;
|
|
41
|
+
linked: boolean;
|
|
42
|
+
};
|
|
34
43
|
}
|
|
35
44
|
export interface WorkspaceRef {
|
|
36
45
|
name: string;
|
|
@@ -49,4 +58,7 @@ export declare class WorkspaceFs {
|
|
|
49
58
|
* (so it stays contained), bounded by a scan budget and a result cap. */
|
|
50
59
|
search(workspace: string, query: string): Promise<SearchResult>;
|
|
51
60
|
gitDiff(workspace: string, relPath?: string): Promise<WorkspaceDiff>;
|
|
61
|
+
/** Branch + worktree context for a repo root. Best-effort: any git hiccup just
|
|
62
|
+
* omits the fields so the diff itself still returns. */
|
|
63
|
+
private gitContext;
|
|
52
64
|
}
|
package/dist/i18n/types.d.ts
CHANGED
|
@@ -672,6 +672,9 @@ export interface ChannelCliMessages {
|
|
|
672
672
|
channelAdded: (type: string) => string;
|
|
673
673
|
cannotRemoveLastEnabled: string;
|
|
674
674
|
channelRemoved: (id: string) => string;
|
|
675
|
+
channelCredentialsCleared: (id: string) => string;
|
|
676
|
+
channelCredentialsClearFailed: (id: string, error: string) => string;
|
|
677
|
+
channelCredentialsKept: (id: string) => string;
|
|
675
678
|
cannotDisableLastEnabled: string;
|
|
676
679
|
channelEnabledToggled: (id: string, enabled: boolean) => string;
|
|
677
680
|
channelReplyModeSet: (id: string, mode: string) => string;
|
package/dist/plugin-api.js
CHANGED
|
@@ -873,6 +873,9 @@ var init_channel_cli = __esm(() => {
|
|
|
873
873
|
channelAdded: (type) => `Channel ${type} added`,
|
|
874
874
|
cannotRemoveLastEnabled: "Cannot remove the last enabled channel.",
|
|
875
875
|
channelRemoved: (id) => `Channel ${id} removed`,
|
|
876
|
+
channelCredentialsCleared: (id) => `Removed stored credentials for channel ${id}`,
|
|
877
|
+
channelCredentialsClearFailed: (id, error) => `Channel ${id} removed, but clearing its stored credentials failed: ${error}`,
|
|
878
|
+
channelCredentialsKept: (id) => `Kept stored credentials for channel ${id} (--keep-credentials)`,
|
|
876
879
|
cannotDisableLastEnabled: "Cannot disable the last enabled channel.",
|
|
877
880
|
channelEnabledToggled: (id, enabled) => `Channel ${id} ${enabled ? "enabled" : "disabled"}`,
|
|
878
881
|
channelReplyModeSet: (id, mode) => `Channel ${id} default reply mode set to: ${mode}`,
|
|
@@ -1965,6 +1968,9 @@ var init_channel_cli2 = __esm(() => {
|
|
|
1965
1968
|
channelAdded: (type) => `频道 ${type} 已添加`,
|
|
1966
1969
|
cannotRemoveLastEnabled: "不能删除最后一个启用的频道。",
|
|
1967
1970
|
channelRemoved: (id) => `频道 ${id} 已删除`,
|
|
1971
|
+
channelCredentialsCleared: (id) => `已移除频道 ${id} 的存储凭证`,
|
|
1972
|
+
channelCredentialsClearFailed: (id, error) => `频道 ${id} 已删除,但清除其存储凭证失败:${error}`,
|
|
1973
|
+
channelCredentialsKept: (id) => `已保留频道 ${id} 的存储凭证(--keep-credentials)`,
|
|
1968
1974
|
cannotDisableLastEnabled: "不能禁用最后一个启用的频道。",
|
|
1969
1975
|
channelEnabledToggled: (id, enabled) => `频道 ${id} 已${enabled ? "启用" : "禁用"}`,
|
|
1970
1976
|
channelReplyModeSet: (id, mode) => `频道 ${id} 的默认 reply mode 已设置为:${mode}`,
|
|
@@ -114,6 +114,16 @@ export interface PromptOptions {
|
|
|
114
114
|
* re-sent on every update (REPLACE, not append). Optional — text channels omit it.
|
|
115
115
|
*/
|
|
116
116
|
onPlan?: (entries: PlanEntry[]) => void | Promise<void>;
|
|
117
|
+
/**
|
|
118
|
+
* Context-usage side-channel: the agent's ACP `usage_update` — `used` tokens
|
|
119
|
+
* currently in context and `size`, the model's total context window. Replace-latest
|
|
120
|
+
* scalar (re-sent during a turn). Optional — only agents that report it fire this
|
|
121
|
+
* (e.g. claude does, codex does not), and text channels omit the handler.
|
|
122
|
+
*/
|
|
123
|
+
onUsage?: (usage: {
|
|
124
|
+
used: number;
|
|
125
|
+
size: number;
|
|
126
|
+
}) => void | Promise<void>;
|
|
117
127
|
/**
|
|
118
128
|
* How tool_call / tool_call_update events are surfaced for this prompt.
|
|
119
129
|
*
|
|
@@ -50,6 +50,11 @@ export interface ChatRequest {
|
|
|
50
50
|
onThought?: (chunk: string) => void | Promise<void>;
|
|
51
51
|
/** Structured plan/todo side-channel; see PromptOptions.onPlan. */
|
|
52
52
|
onPlan?: (entries: PlanEntry[]) => void | Promise<void>;
|
|
53
|
+
/** Context-usage side-channel; see PromptOptions.onUsage. */
|
|
54
|
+
onUsage?: (usage: {
|
|
55
|
+
used: number;
|
|
56
|
+
size: number;
|
|
57
|
+
}) => void | Promise<void>;
|
|
53
58
|
/**
|
|
54
59
|
* Optional per-turn performance tracing span. When `logging.perf.enabled` is
|
|
55
60
|
* true, the channel handler attaches a `PerfSpan` so downstream layers can
|