@ganglion/xacpx 0.13.0 → 0.14.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/README.md +79 -460
- package/dist/bridge/bridge-main.js +112 -5
- package/dist/cli.js +436 -101
- package/dist/commands/handlers/session-handler.d.ts +3 -9
- package/dist/commands/router-types.d.ts +2 -5
- package/dist/control/control-event-bus.d.ts +10 -0
- package/dist/control/control-service.d.ts +15 -0
- package/dist/control/upload-store.d.ts +28 -0
- package/dist/i18n/types.d.ts +1 -0
- package/dist/plugin-api.js +2 -0
- package/dist/transport/types.d.ts +37 -4
- package/dist/weixin/agent/interface.d.ts +4 -4
- package/package.json +2 -2
|
@@ -72,6 +72,10 @@ function encodeBridgePromptUsageEvent(event) {
|
|
|
72
72
|
return `${JSON.stringify(event)}
|
|
73
73
|
`;
|
|
74
74
|
}
|
|
75
|
+
function encodeBridgePromptCommandsEvent(event) {
|
|
76
|
+
return `${JSON.stringify(event)}
|
|
77
|
+
`;
|
|
78
|
+
}
|
|
75
79
|
function encodeBridgeSessionProgressEvent(event) {
|
|
76
80
|
return `${JSON.stringify(event)}
|
|
77
81
|
`;
|
|
@@ -429,6 +433,7 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
429
433
|
let onThought;
|
|
430
434
|
let onPlan;
|
|
431
435
|
let onUsage;
|
|
436
|
+
let onCommands;
|
|
432
437
|
let rawStream = false;
|
|
433
438
|
if (options === undefined) {
|
|
434
439
|
toolEventMode = "text";
|
|
@@ -441,6 +446,7 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
441
446
|
onThought = options.onThought;
|
|
442
447
|
onPlan = options.onPlan;
|
|
443
448
|
onUsage = options.onUsage;
|
|
449
|
+
onCommands = options.onCommands;
|
|
444
450
|
rawStream = options.rawStream ?? false;
|
|
445
451
|
toolEventMode = resolveToolEventMode({
|
|
446
452
|
toolEventMode: options.mode,
|
|
@@ -454,12 +460,14 @@ function createStreamingPromptState(formatToolCalls = false, options) {
|
|
|
454
460
|
pendingLine: "",
|
|
455
461
|
formatToolCalls,
|
|
456
462
|
emittedToolCallIds: new Set,
|
|
463
|
+
toolCalls: new Map,
|
|
457
464
|
toolEventMode,
|
|
458
465
|
rawStream,
|
|
459
466
|
onToolEvent,
|
|
460
467
|
onThought,
|
|
461
468
|
onPlan,
|
|
462
469
|
onUsage,
|
|
470
|
+
onCommands,
|
|
463
471
|
finalize() {
|
|
464
472
|
if (this.pendingLine.trim().length > 0) {
|
|
465
473
|
parseStreamingChunks(this, this.pendingLine);
|
|
@@ -500,7 +508,8 @@ function parseStreamingChunks(state, line) {
|
|
|
500
508
|
const wantsStructured = state.toolEventMode === "structured" || state.toolEventMode === "both";
|
|
501
509
|
const wantsText = (state.toolEventMode === "text" || state.toolEventMode === "both") && state.formatToolCalls;
|
|
502
510
|
if (wantsStructured && state.onToolEvent) {
|
|
503
|
-
const
|
|
511
|
+
const merged = update.toolCallId ? mergeToolCallUpdate(state, update.toolCallId, update) : update;
|
|
512
|
+
const toolEvent = buildToolUseEvent(merged);
|
|
504
513
|
if (toolEvent)
|
|
505
514
|
state.onToolEvent(toolEvent);
|
|
506
515
|
}
|
|
@@ -527,8 +536,17 @@ function parseStreamingChunks(state, line) {
|
|
|
527
536
|
if (update.sessionUpdate === "usage_update") {
|
|
528
537
|
const used = typeof update.used === "number" && Number.isFinite(update.used) ? update.used : undefined;
|
|
529
538
|
const size = typeof update.size === "number" && Number.isFinite(update.size) ? update.size : undefined;
|
|
530
|
-
if (used !== undefined && size !== undefined && size > 0)
|
|
531
|
-
|
|
539
|
+
if (used !== undefined && size !== undefined && size > 0) {
|
|
540
|
+
const cost = normalizeUsageCost(update.cost);
|
|
541
|
+
const breakdown = normalizeUsageBreakdown(update._meta?.usage);
|
|
542
|
+
state.onUsage?.({ used, size, ...cost ? { cost } : {}, ...breakdown ? { breakdown } : {} });
|
|
543
|
+
}
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
if (update.sessionUpdate === "available_commands_update") {
|
|
547
|
+
if (Array.isArray(update.availableCommands)) {
|
|
548
|
+
state.onCommands?.(normalizeAgentCommands(update.availableCommands));
|
|
549
|
+
}
|
|
532
550
|
return;
|
|
533
551
|
}
|
|
534
552
|
const isThoughtChunk = update.sessionUpdate === "agent_thought_chunk" && update.content?.type === "text" && typeof update.content.text === "string";
|
|
@@ -578,6 +596,29 @@ function formatToolCallEvent(update, sessionUpdate) {
|
|
|
578
596
|
const statusText = status ? ` (${status})` : "";
|
|
579
597
|
return `${emoji} ${title}${statusText}${summaryText}`;
|
|
580
598
|
}
|
|
599
|
+
function isEmptyToolField(v) {
|
|
600
|
+
if (v === undefined || v === null)
|
|
601
|
+
return true;
|
|
602
|
+
if (typeof v === "string")
|
|
603
|
+
return v.trim().length === 0;
|
|
604
|
+
if (Array.isArray(v))
|
|
605
|
+
return v.length === 0;
|
|
606
|
+
if (typeof v === "object")
|
|
607
|
+
return Object.keys(v).length === 0;
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
function mergeToolCallUpdate(state, toolCallId, update) {
|
|
611
|
+
const prev = state.toolCalls.get(toolCallId) ?? { toolCallId };
|
|
612
|
+
const merged = { ...prev };
|
|
613
|
+
for (const key of ["kind", "title", "rawInput", "content", "rawOutput", "locations", "status"]) {
|
|
614
|
+
const next = update[key];
|
|
615
|
+
if (!isEmptyToolField(next))
|
|
616
|
+
merged[key] = next;
|
|
617
|
+
}
|
|
618
|
+
merged.toolCallId = toolCallId;
|
|
619
|
+
state.toolCalls.set(toolCallId, merged);
|
|
620
|
+
return merged;
|
|
621
|
+
}
|
|
581
622
|
function buildToolUseEvent(update) {
|
|
582
623
|
if (!update)
|
|
583
624
|
return null;
|
|
@@ -695,6 +736,52 @@ function readFirstStringArray(record, keys) {
|
|
|
695
736
|
}
|
|
696
737
|
return;
|
|
697
738
|
}
|
|
739
|
+
function asFiniteNumber(value) {
|
|
740
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
741
|
+
}
|
|
742
|
+
function firstFiniteNumber(record, keys) {
|
|
743
|
+
for (const key of keys) {
|
|
744
|
+
const n = asFiniteNumber(record[key]);
|
|
745
|
+
if (n !== undefined)
|
|
746
|
+
return n;
|
|
747
|
+
}
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
function normalizeUsageBreakdown(value) {
|
|
751
|
+
if (!isRecord(value))
|
|
752
|
+
return;
|
|
753
|
+
const out = {};
|
|
754
|
+
for (const [key, aliases] of USAGE_BREAKDOWN_FIELDS) {
|
|
755
|
+
const n = firstFiniteNumber(value, aliases);
|
|
756
|
+
if (n !== undefined)
|
|
757
|
+
out[key] = n;
|
|
758
|
+
}
|
|
759
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
760
|
+
}
|
|
761
|
+
function normalizeUsageCost(value) {
|
|
762
|
+
if (!isRecord(value))
|
|
763
|
+
return;
|
|
764
|
+
const amount = asFiniteNumber(value.amount);
|
|
765
|
+
const currency = readString(value, "currency");
|
|
766
|
+
if (amount === undefined && !currency)
|
|
767
|
+
return;
|
|
768
|
+
return { ...amount !== undefined ? { amount } : {}, ...currency ? { currency } : {} };
|
|
769
|
+
}
|
|
770
|
+
function normalizeAgentCommands(value) {
|
|
771
|
+
if (!Array.isArray(value))
|
|
772
|
+
return [];
|
|
773
|
+
const out = [];
|
|
774
|
+
for (const entry of value) {
|
|
775
|
+
if (!isRecord(entry))
|
|
776
|
+
continue;
|
|
777
|
+
const name = readString(entry, "name");
|
|
778
|
+
if (!name)
|
|
779
|
+
continue;
|
|
780
|
+
const description = readString(entry, "description");
|
|
781
|
+
out.push({ name, ...description ? { description } : {}, hasInput: entry.input != null });
|
|
782
|
+
}
|
|
783
|
+
return out;
|
|
784
|
+
}
|
|
698
785
|
function isRecord(value) {
|
|
699
786
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
700
787
|
}
|
|
@@ -720,8 +807,17 @@ function isGenericToolTitle(kind, title) {
|
|
|
720
807
|
}
|
|
721
808
|
return false;
|
|
722
809
|
}
|
|
810
|
+
var USAGE_BREAKDOWN_FIELDS;
|
|
723
811
|
var init_streaming_prompt = __esm(() => {
|
|
724
812
|
init_tool_kind_emoji();
|
|
813
|
+
USAGE_BREAKDOWN_FIELDS = [
|
|
814
|
+
["inputTokens", ["inputTokens", "input_tokens"]],
|
|
815
|
+
["outputTokens", ["outputTokens", "output_tokens"]],
|
|
816
|
+
["cachedReadTokens", ["cachedReadTokens", "cacheReadInputTokens", "cache_read_input_tokens"]],
|
|
817
|
+
["cachedWriteTokens", ["cachedWriteTokens", "cacheCreationInputTokens", "cache_creation_input_tokens"]],
|
|
818
|
+
["thoughtTokens", ["thoughtTokens", "thought_tokens"]],
|
|
819
|
+
["totalTokens", ["totalTokens", "total_tokens"]]
|
|
820
|
+
];
|
|
725
821
|
});
|
|
726
822
|
|
|
727
823
|
// src/recovery/discover-parent-package-paths.ts
|
|
@@ -1874,6 +1970,7 @@ var init_misc = __esm(() => {
|
|
|
1874
1970
|
defaultHomeWorkspaceDescription: "Home directory",
|
|
1875
1971
|
pluginChannelFeishu: "Feishu channel",
|
|
1876
1972
|
pluginChannelYuanbao: "Tencent Yuanbao channel",
|
|
1973
|
+
pluginChannelRelay: "Relay hub connector (drive this instance from a self-hosted relay hub)",
|
|
1877
1974
|
pluginChannelInstallHint: (channelType, packageName) => `Channel ${channelType} requires a plugin: xacpx plugin add ${packageName}`,
|
|
1878
1975
|
orchestrationSuggestion1: "Run /tasks --stuck to locate stuck tasks",
|
|
1879
1976
|
orchestrationSuggestion2: "/task <id> shows the full timeline to locate errors",
|
|
@@ -2970,6 +3067,7 @@ var init_misc2 = __esm(() => {
|
|
|
2970
3067
|
defaultHomeWorkspaceDescription: "用户主目录",
|
|
2971
3068
|
pluginChannelFeishu: "飞书频道",
|
|
2972
3069
|
pluginChannelYuanbao: "腾讯元宝频道",
|
|
3070
|
+
pluginChannelRelay: "Relay hub 连接器(从自托管 relay hub 遥控这台实例)",
|
|
2973
3071
|
pluginChannelInstallHint: (channelType, packageName) => `频道 ${channelType} 需要安装插件:xacpx plugin add ${packageName}`,
|
|
2974
3072
|
orchestrationSuggestion1: "查看 /tasks --stuck 定位卡住的任务",
|
|
2975
3073
|
orchestrationSuggestion2: "/task <id> 可看完整时间线定位错误点",
|
|
@@ -4049,7 +4147,8 @@ async function runStreamingPrompt(command, args, onEvent, options = {}) {
|
|
|
4049
4147
|
...onEvent && (toolEventMode === "structured" || toolEventMode === "both") ? { onToolEvent: (toolEvent) => onEvent({ type: "prompt.tool_event", event: toolEvent }) } : {},
|
|
4050
4148
|
...onEvent ? { onThought: (chunk) => onEvent({ type: "prompt.thought", text: chunk }) } : {},
|
|
4051
4149
|
...onEvent ? { onPlan: (entries) => onEvent({ type: "prompt.plan", entries }) } : {},
|
|
4052
|
-
...onEvent ? { onUsage: (usage) => onEvent({ type: "prompt.usage", used: usage.used, size: usage.size }) } : {}
|
|
4150
|
+
...onEvent ? { onUsage: (usage) => onEvent({ type: "prompt.usage", used: usage.used, size: usage.size, ...usage.cost ? { cost: usage.cost } : {}, ...usage.breakdown ? { breakdown: usage.breakdown } : {} }) } : {},
|
|
4151
|
+
...onEvent ? { onCommands: (commands) => onEvent({ type: "prompt.commands", commands }) } : {}
|
|
4053
4152
|
});
|
|
4054
4153
|
let lastReplyAt = now();
|
|
4055
4154
|
const flushBuffer = () => {
|
|
@@ -4347,7 +4446,15 @@ class BridgeServer {
|
|
|
4347
4446
|
id: requestId,
|
|
4348
4447
|
event: "prompt.usage",
|
|
4349
4448
|
used: event.used,
|
|
4350
|
-
size: event.size
|
|
4449
|
+
size: event.size,
|
|
4450
|
+
...event.cost ? { cost: event.cost } : {},
|
|
4451
|
+
...event.breakdown ? { breakdown: event.breakdown } : {}
|
|
4452
|
+
}));
|
|
4453
|
+
} else if (event.type === "prompt.commands") {
|
|
4454
|
+
writeLine?.(encodeBridgePromptCommandsEvent({
|
|
4455
|
+
id: requestId,
|
|
4456
|
+
event: "prompt.commands",
|
|
4457
|
+
commands: event.commands
|
|
4351
4458
|
}));
|
|
4352
4459
|
}
|
|
4353
4460
|
});
|