@hirey/hi-mcp-server 0.1.6 → 0.1.8
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/openclaw-session-key.d.ts +13 -0
- package/dist/openclaw-session-key.d.ts.map +1 -0
- package/dist/openclaw-session-key.js +76 -0
- package/dist/receiver-command.d.ts +9 -0
- package/dist/receiver-command.d.ts.map +1 -0
- package/dist/receiver-command.js +16 -0
- package/dist/server.js +133 -37
- package/package.json +1 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type OpenClawSessionKeyValidation = {
|
|
2
|
+
ok: true;
|
|
3
|
+
normalized: string;
|
|
4
|
+
agentId: string;
|
|
5
|
+
rest: string;
|
|
6
|
+
} | {
|
|
7
|
+
ok: false;
|
|
8
|
+
reason: 'status_display_value' | 'sessions_display_value' | 'non_canonical';
|
|
9
|
+
input: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function looksLikeOpenClawSessionKey(input: unknown): boolean;
|
|
12
|
+
export declare function validateOpenClawSessionKey(input: unknown): OpenClawSessionKeyValidation | null;
|
|
13
|
+
//# sourceMappingURL=openclaw-session-key.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openclaw-session-key.d.ts","sourceRoot":"","sources":["../src/openclaw-session-key.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,4BAA4B,GACpC;IACE,EAAE,EAAE,IAAI,CAAC;IACT,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd,GACD;IACE,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,sBAAsB,GAAG,wBAAwB,GAAG,eAAe,CAAC;IAC5E,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AA0CN,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAQnE;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,4BAA4B,GAAG,IAAI,CAiC9F"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
function parseCanonicalAgentSessionKey(input) {
|
|
2
|
+
const raw = input.trim().toLowerCase();
|
|
3
|
+
if (!raw) {
|
|
4
|
+
return null;
|
|
5
|
+
}
|
|
6
|
+
const parts = raw.split(':').filter(Boolean);
|
|
7
|
+
if (parts.length < 3 || parts[0] !== 'agent') {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const agentId = parts[1]?.trim();
|
|
11
|
+
const rest = parts.slice(2).join(':');
|
|
12
|
+
if (!agentId || !rest) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
normalized: `agent:${agentId}:${rest}`,
|
|
17
|
+
agentId,
|
|
18
|
+
rest,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function looksLikeStatusDisplayValue(input) {
|
|
22
|
+
// `openclaw status` textual tables shorten long keys with a trailing Unicode ellipsis.
|
|
23
|
+
return input.includes('…');
|
|
24
|
+
}
|
|
25
|
+
function looksLikeSessionsDisplayValue(input) {
|
|
26
|
+
// `openclaw sessions` currently renders long keys as:
|
|
27
|
+
// <16-char head>...<6-char tail>
|
|
28
|
+
// Keep the detector tight so we only reject the known display artifact.
|
|
29
|
+
const trimmed = input.trim();
|
|
30
|
+
return trimmed.toLowerCase().startsWith('agent:')
|
|
31
|
+
&& trimmed.length === 25
|
|
32
|
+
&& trimmed.slice(16, 19) === '...';
|
|
33
|
+
}
|
|
34
|
+
export function looksLikeOpenClawSessionKey(input) {
|
|
35
|
+
const trimmed = String(input ?? '').trim();
|
|
36
|
+
if (!trimmed) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return trimmed.toLowerCase().startsWith('agent:')
|
|
40
|
+
|| looksLikeStatusDisplayValue(trimmed)
|
|
41
|
+
|| looksLikeSessionsDisplayValue(trimmed);
|
|
42
|
+
}
|
|
43
|
+
export function validateOpenClawSessionKey(input) {
|
|
44
|
+
const trimmed = String(input ?? '').trim();
|
|
45
|
+
if (!trimmed) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
if (looksLikeStatusDisplayValue(trimmed)) {
|
|
49
|
+
return {
|
|
50
|
+
ok: false,
|
|
51
|
+
reason: 'status_display_value',
|
|
52
|
+
input: trimmed,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (looksLikeSessionsDisplayValue(trimmed)) {
|
|
56
|
+
return {
|
|
57
|
+
ok: false,
|
|
58
|
+
reason: 'sessions_display_value',
|
|
59
|
+
input: trimmed,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const parsed = parseCanonicalAgentSessionKey(trimmed);
|
|
63
|
+
if (!parsed) {
|
|
64
|
+
return {
|
|
65
|
+
ok: false,
|
|
66
|
+
reason: 'non_canonical',
|
|
67
|
+
input: trimmed,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
ok: true,
|
|
72
|
+
normalized: parsed.normalized,
|
|
73
|
+
agentId: parsed.agentId,
|
|
74
|
+
rest: parsed.rest,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function resolveCanonicalOpenClawReceiverBinaryPath(homeDir?: string): string;
|
|
2
|
+
export declare function buildInstallReceiverCommandArgv(args: {
|
|
3
|
+
explicitArgv: string[];
|
|
4
|
+
receiverCommand: string;
|
|
5
|
+
hostKind: 'openclaw' | 'generic';
|
|
6
|
+
enableLocalReceiver: boolean;
|
|
7
|
+
homeDir?: string;
|
|
8
|
+
}): string[];
|
|
9
|
+
//# sourceMappingURL=receiver-command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receiver-command.d.ts","sourceRoot":"","sources":["../src/receiver-command.ts"],"names":[],"mappings":"AAGA,wBAAgB,0CAA0C,CAAC,OAAO,GAAE,MAAqB,GAAG,MAAM,CAEjG;AAED,wBAAgB,+BAA+B,CAAC,IAAI,EAAE;IACpD,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,UAAU,GAAG,SAAS,CAAC;IACjC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,MAAM,EAAE,CAQX"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export function resolveCanonicalOpenClawReceiverBinaryPath(homeDir = os.homedir()) {
|
|
4
|
+
return path.join(homeDir, '.openclaw', 'vendor', 'hi', 'node_modules', '.bin', 'hi-agent-receiver');
|
|
5
|
+
}
|
|
6
|
+
export function buildInstallReceiverCommandArgv(args) {
|
|
7
|
+
if (args.explicitArgv.length > 0)
|
|
8
|
+
return args.explicitArgv;
|
|
9
|
+
if (args.receiverCommand)
|
|
10
|
+
return [args.receiverCommand];
|
|
11
|
+
// OpenClaw's public install path is user-local vendor install, not global PATH.
|
|
12
|
+
if (args.hostKind === 'openclaw' && args.enableLocalReceiver) {
|
|
13
|
+
return [resolveCanonicalOpenClawReceiverBinaryPath(args.homeDir)];
|
|
14
|
+
}
|
|
15
|
+
return ['hi-agent-receiver'];
|
|
16
|
+
}
|
package/dist/server.js
CHANGED
|
@@ -12,6 +12,8 @@ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextpro
|
|
|
12
12
|
import { AGENT_GATEWAY_EVENT_TOPICS, normalizeAgentEndpointList, normalizeAgentInstallationDeliveryDeclaration, normalizeAgentSubscriptionList, normalizeText, } from '@hirey/hi-agent-contracts';
|
|
13
13
|
import { createHiAgentClients, exchangeHiAgentClientCredentialsToken, HiAgentGatewayClient, HiAgentPlatformClient, } from '@hirey/hi-agent-sdk';
|
|
14
14
|
import { readState, resolveCanonicalOpenClawStateDir, resolveDefaultStateDir, resolveLegacyStateFiles, resolveStateFile, updateState, normalizeStateProfile, } from './state.js';
|
|
15
|
+
import { looksLikeOpenClawSessionKey, validateOpenClawSessionKey, } from './openclaw-session-key.js';
|
|
16
|
+
import { buildInstallReceiverCommandArgv, } from './receiver-command.js';
|
|
15
17
|
const CAPABILITY_CACHE_TTL_MS = 30_000;
|
|
16
18
|
const resolvedProfile = normalizeStateProfile(process.env.HI_MCP_PROFILE);
|
|
17
19
|
const config = {
|
|
@@ -107,7 +109,7 @@ function controlTools() {
|
|
|
107
109
|
host_adapter_url: { type: 'string', description: 'enable_local_receiver=true 时可选:宿主本地接收入口。OpenClaw 默认 full endpoint 为 http://127.0.0.1:18789/hooks/agent;注意 OpenClaw 配置里的 hooks.path 应保持 /hooks,而不是 /hooks/agent。' },
|
|
108
110
|
host_adapter_bearer_token: { type: 'string', description: 'host_adapter_kind=openclaw_hooks 时必填:本地 hooks bearer token。' },
|
|
109
111
|
openresponses_model: { type: 'string', description: 'host_adapter_kind=openresponses 时必填:receiver 发给本地入口使用的 model。' },
|
|
110
|
-
host_session_key: { type: 'string', description: '可选:把当前 OpenClaw
|
|
112
|
+
host_session_key: { type: 'string', description: '可选:把当前 OpenClaw 当前会话显式设为 default continuation route 的 canonical full session_key。只能使用结构化宿主来源返回的完整 key;不要从 openclaw status / openclaw sessions / TUI footer 这类展示文本里抄值。' },
|
|
111
113
|
route_missing_policy: { type: 'string', description: "可选:'use_explicit_default_route'|'fail_closed'。默认在提供 host_session_key/default_reply_* 时使用 use_explicit_default_route,否则为 null。" },
|
|
112
114
|
default_reply_channel: { type: 'string', description: '可选:default continuation route 的 channel。' },
|
|
113
115
|
default_reply_to: { type: 'string', description: '可选:default continuation route 的宿主 target。' },
|
|
@@ -535,30 +537,91 @@ function buildInstallationDeliveryDeclaration(args) {
|
|
|
535
537
|
function hasActiveInstallationCapability(declaration, kind) {
|
|
536
538
|
return !!declaration?.capabilities?.some((entry) => entry.kind === kind && entry.status !== 'disabled');
|
|
537
539
|
}
|
|
538
|
-
function
|
|
540
|
+
function buildInvalidOpenClawSessionKeyError(reason, input) {
|
|
541
|
+
const error = new Error('invalid_openclaw_host_session_key');
|
|
542
|
+
const hint = reason === 'status_display_value'
|
|
543
|
+
? 'Use a structured host source for the full session key; do not copy the truncated key shown by openclaw status.'
|
|
544
|
+
: reason === 'sessions_display_value'
|
|
545
|
+
? 'Use a structured host source for the full session key; do not copy the truncated key shown by openclaw sessions.'
|
|
546
|
+
: 'Expected an OpenClaw canonical full session key shaped like agent:<agent_id>:<rest>.';
|
|
547
|
+
error.detail = {
|
|
548
|
+
reason,
|
|
549
|
+
input,
|
|
550
|
+
hint,
|
|
551
|
+
};
|
|
552
|
+
return error;
|
|
553
|
+
}
|
|
554
|
+
function normalizeReplyRouteSessionKey(input, options = {}) {
|
|
555
|
+
const sessionKey = normalizeText(input) || null;
|
|
556
|
+
if (!sessionKey) {
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
const shouldValidateAsOpenClaw = options.requireOpenClawSessionKey || looksLikeOpenClawSessionKey(sessionKey);
|
|
560
|
+
if (!shouldValidateAsOpenClaw) {
|
|
561
|
+
return sessionKey;
|
|
562
|
+
}
|
|
563
|
+
const validation = validateOpenClawSessionKey(sessionKey);
|
|
564
|
+
if (!validation) {
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
if (!validation.ok) {
|
|
568
|
+
throw buildInvalidOpenClawSessionKeyError(validation.reason, validation.input);
|
|
569
|
+
}
|
|
570
|
+
return validation.normalized;
|
|
571
|
+
}
|
|
572
|
+
function normalizeReplyRouteWithInstallationId(input, installationId, options = {}) {
|
|
539
573
|
if (!isPlainObject(input))
|
|
540
574
|
return null;
|
|
541
575
|
const normalizedInstallationId = normalizeText(installationId) || null;
|
|
542
576
|
const existingInstallationId = normalizeText(input.installation_id) || null;
|
|
543
|
-
|
|
577
|
+
const hasSessionKey = Object.prototype.hasOwnProperty.call(input, 'session_key')
|
|
578
|
+
|| Object.prototype.hasOwnProperty.call(input, 'sessionKey');
|
|
579
|
+
const normalizedSessionKey = hasSessionKey
|
|
580
|
+
? normalizeReplyRouteSessionKey(Object.prototype.hasOwnProperty.call(input, 'session_key')
|
|
581
|
+
? input.session_key
|
|
582
|
+
: input.sessionKey, options)
|
|
583
|
+
: undefined;
|
|
584
|
+
if (!normalizedInstallationId && !hasSessionKey)
|
|
544
585
|
return input;
|
|
545
|
-
|
|
586
|
+
const output = {
|
|
546
587
|
...input,
|
|
547
|
-
installation_id: normalizedInstallationId,
|
|
548
588
|
};
|
|
589
|
+
if (normalizedInstallationId && !existingInstallationId) {
|
|
590
|
+
output.installation_id = normalizedInstallationId;
|
|
591
|
+
}
|
|
592
|
+
if (hasSessionKey) {
|
|
593
|
+
if (Object.prototype.hasOwnProperty.call(output, 'session_key')) {
|
|
594
|
+
output.session_key = normalizedSessionKey;
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
output.sessionKey = normalizedSessionKey;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return output;
|
|
549
601
|
}
|
|
550
|
-
function
|
|
551
|
-
if (!
|
|
552
|
-
return
|
|
602
|
+
function applyNormalizedDefaultReplyRoute(input, installationId, options = {}) {
|
|
603
|
+
if (!Object.prototype.hasOwnProperty.call(input, 'default_reply_route')) {
|
|
604
|
+
return input;
|
|
553
605
|
}
|
|
554
|
-
|
|
606
|
+
return {
|
|
555
607
|
...input,
|
|
556
|
-
default_reply_route:
|
|
608
|
+
default_reply_route: normalizeReplyRouteWithInstallationId(input.default_reply_route, installationId, options),
|
|
557
609
|
};
|
|
558
|
-
return normalizeAgentInstallationDeliveryDeclaration(hydrated);
|
|
559
610
|
}
|
|
560
|
-
function
|
|
561
|
-
|
|
611
|
+
function normalizeDeliveryCapabilitiesWithInstallationId(input, installationId) {
|
|
612
|
+
if (isPlainObject(input)) {
|
|
613
|
+
return applyNormalizedDefaultReplyRoute(input, installationId);
|
|
614
|
+
}
|
|
615
|
+
const normalized = normalizeAgentInstallationDeliveryDeclaration(input);
|
|
616
|
+
if (!isPlainObject(normalized)) {
|
|
617
|
+
return normalized;
|
|
618
|
+
}
|
|
619
|
+
return applyNormalizedDefaultReplyRoute(normalized, installationId);
|
|
620
|
+
}
|
|
621
|
+
function buildDefaultReplyRoute(args, options = {}) {
|
|
622
|
+
const sessionKey = normalizeReplyRouteSessionKey(args.host_session_key, {
|
|
623
|
+
requireOpenClawSessionKey: options.requireOpenClawSessionKey,
|
|
624
|
+
});
|
|
562
625
|
const deliveryContext = {
|
|
563
626
|
channel: normalizeText(args.default_reply_channel) || null,
|
|
564
627
|
to: normalizeText(args.default_reply_to) || null,
|
|
@@ -572,7 +635,7 @@ function buildDefaultReplyRoute(args, installationId) {
|
|
|
572
635
|
if (!sessionKey && !hasDeliveryContext)
|
|
573
636
|
return null;
|
|
574
637
|
return {
|
|
575
|
-
installation_id: normalizeText(installationId) || null,
|
|
638
|
+
installation_id: normalizeText(options.installationId) || null,
|
|
576
639
|
session_key: sessionKey,
|
|
577
640
|
delivery_context: hasDeliveryContext ? deliveryContext : null,
|
|
578
641
|
};
|
|
@@ -693,9 +756,11 @@ function buildDoctorSummary(args) {
|
|
|
693
756
|
const defaultReplyRoute = isPlainObject(deliveryDeclaration?.default_reply_route)
|
|
694
757
|
? deliveryDeclaration.default_reply_route
|
|
695
758
|
: null;
|
|
696
|
-
const continuityState = defaultReplyRoute
|
|
697
|
-
? '
|
|
698
|
-
:
|
|
759
|
+
const continuityState = defaultReplyRoute && !args.defaultReplyRouteValidation.ok
|
|
760
|
+
? 'explicit_default_route_invalid'
|
|
761
|
+
: defaultReplyRoute
|
|
762
|
+
? 'explicit_default_route_ready'
|
|
763
|
+
: (routeMissingPolicy === 'fail_closed' ? 'origin_capture_only' : 'continuity_not_ready');
|
|
699
764
|
return {
|
|
700
765
|
ok: args.blockers.length === 0,
|
|
701
766
|
profile: config.profile,
|
|
@@ -715,6 +780,7 @@ function buildDoctorSummary(args) {
|
|
|
715
780
|
delivery_capabilities: deliveryDeclaration,
|
|
716
781
|
route_missing_policy: routeMissingPolicy,
|
|
717
782
|
default_reply_route: defaultReplyRoute,
|
|
783
|
+
default_reply_route_validation: args.defaultReplyRouteValidation,
|
|
718
784
|
delivery_probe: args.deliveryProbe,
|
|
719
785
|
delivery_probe_diagnostics: args.deliveryProbeDiagnostics,
|
|
720
786
|
};
|
|
@@ -806,10 +872,8 @@ async function handleRegister(args) {
|
|
|
806
872
|
capabilities: args.capabilities,
|
|
807
873
|
metadata: isPlainObject(args.metadata) ? args.metadata : null,
|
|
808
874
|
// register 阶段 installation_id 由 gateway 正式生成;
|
|
809
|
-
//
|
|
810
|
-
delivery_capabilities:
|
|
811
|
-
? args.delivery_capabilities
|
|
812
|
-
: normalizeAgentInstallationDeliveryDeclaration(args.delivery_capabilities),
|
|
875
|
+
// 这里仍然保留 default_reply_route / route_missing_policy,但会先做 reply route 校验。
|
|
876
|
+
delivery_capabilities: normalizeDeliveryCapabilitiesWithInstallationId(args.delivery_capabilities, null),
|
|
813
877
|
status: normalizeText(args.status) || undefined,
|
|
814
878
|
};
|
|
815
879
|
if (agentId)
|
|
@@ -963,6 +1027,11 @@ async function handleDoctor(args) {
|
|
|
963
1027
|
let remote = null;
|
|
964
1028
|
let deliveryProbe = null;
|
|
965
1029
|
let deliveryProbeDiagnostics = null;
|
|
1030
|
+
let defaultReplyRouteValidation = {
|
|
1031
|
+
ok: true,
|
|
1032
|
+
reason: null,
|
|
1033
|
+
session_key: null,
|
|
1034
|
+
};
|
|
966
1035
|
if (!state.identity) {
|
|
967
1036
|
blockers.push('missing_agent_identity');
|
|
968
1037
|
}
|
|
@@ -985,6 +1054,35 @@ async function handleDoctor(args) {
|
|
|
985
1054
|
subscriptions: subscriptions.subscriptions,
|
|
986
1055
|
};
|
|
987
1056
|
const declaration = installation.installation.delivery_capabilities || null;
|
|
1057
|
+
const defaultReplyRoute = isPlainObject(declaration?.default_reply_route)
|
|
1058
|
+
? declaration.default_reply_route
|
|
1059
|
+
: null;
|
|
1060
|
+
const defaultReplyRouteSessionKey = normalizeText(defaultReplyRoute?.session_key || defaultReplyRoute?.sessionKey) || null;
|
|
1061
|
+
if (defaultReplyRouteSessionKey && looksLikeOpenClawSessionKey(defaultReplyRouteSessionKey)) {
|
|
1062
|
+
const validation = validateOpenClawSessionKey(defaultReplyRouteSessionKey);
|
|
1063
|
+
if (validation?.ok) {
|
|
1064
|
+
defaultReplyRouteValidation = {
|
|
1065
|
+
ok: true,
|
|
1066
|
+
reason: null,
|
|
1067
|
+
session_key: validation.normalized,
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
else if (validation) {
|
|
1071
|
+
defaultReplyRouteValidation = {
|
|
1072
|
+
ok: false,
|
|
1073
|
+
reason: validation.reason,
|
|
1074
|
+
session_key: validation.input,
|
|
1075
|
+
};
|
|
1076
|
+
blockers.push(`openclaw_default_reply_route_session_key_invalid:${validation.reason}`);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
else {
|
|
1080
|
+
defaultReplyRouteValidation = {
|
|
1081
|
+
ok: true,
|
|
1082
|
+
reason: null,
|
|
1083
|
+
session_key: defaultReplyRouteSessionKey,
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
988
1086
|
const activeTopics = new Set((subscriptions.subscriptions || [])
|
|
989
1087
|
.filter((entry) => normalizeText(entry?.status) === 'active')
|
|
990
1088
|
.map((entry) => normalizeText(entry?.topic))
|
|
@@ -1044,6 +1142,7 @@ async function handleDoctor(args) {
|
|
|
1044
1142
|
warnings,
|
|
1045
1143
|
deliveryProbe,
|
|
1046
1144
|
deliveryProbeDiagnostics,
|
|
1145
|
+
defaultReplyRouteValidation,
|
|
1047
1146
|
}));
|
|
1048
1147
|
}
|
|
1049
1148
|
async function handleInstall(args) {
|
|
@@ -1054,13 +1153,12 @@ async function handleInstall(args) {
|
|
|
1054
1153
|
const receiverTransport = normalizeReceiverTransport(args.receiver_transport);
|
|
1055
1154
|
const subscribeDefaultTopics = args.subscribe_default_topics !== false;
|
|
1056
1155
|
const receiverShouldStart = enableLocalReceiver ? args.receiver_start !== false : false;
|
|
1057
|
-
const receiverCommandArgv = (
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
})();
|
|
1156
|
+
const receiverCommandArgv = buildInstallReceiverCommandArgv({
|
|
1157
|
+
explicitArgv: normalizeCommandArgv(args.receiver_command_argv),
|
|
1158
|
+
receiverCommand: normalizeText(args.receiver_command),
|
|
1159
|
+
hostKind,
|
|
1160
|
+
enableLocalReceiver,
|
|
1161
|
+
});
|
|
1064
1162
|
const hostAdapterKindRaw = normalizeText(args.host_adapter_kind).toLowerCase();
|
|
1065
1163
|
const hostAdapterKind = hostAdapterKindRaw === 'openresponses'
|
|
1066
1164
|
? 'openresponses'
|
|
@@ -1103,7 +1201,10 @@ async function handleInstall(args) {
|
|
|
1103
1201
|
state = await loadPersistedState();
|
|
1104
1202
|
}
|
|
1105
1203
|
const { gateway } = await createAuthorizedClients();
|
|
1106
|
-
const defaultReplyRoute = buildDefaultReplyRoute(args,
|
|
1204
|
+
const defaultReplyRoute = buildDefaultReplyRoute(args, {
|
|
1205
|
+
installationId: state.identity?.installation_id || null,
|
|
1206
|
+
requireOpenClawSessionKey: hostKind === 'openclaw',
|
|
1207
|
+
});
|
|
1107
1208
|
const routeMissingPolicy = normalizeText(args.route_missing_policy)
|
|
1108
1209
|
|| (defaultReplyRoute ? 'use_explicit_default_route' : '');
|
|
1109
1210
|
const desiredDeliveryCapabilities = buildInstallationDeliveryDeclaration({
|
|
@@ -1289,13 +1390,8 @@ async function handleInstallationUpdate(args) {
|
|
|
1289
1390
|
: {}),
|
|
1290
1391
|
...(Object.prototype.hasOwnProperty.call(args, 'delivery_capabilities')
|
|
1291
1392
|
// installation_update 需要保留 default_reply_route / route_missing_policy 这类扩展字段,
|
|
1292
|
-
//
|
|
1293
|
-
? { delivery_capabilities: (
|
|
1294
|
-
? {
|
|
1295
|
-
...args.delivery_capabilities,
|
|
1296
|
-
default_reply_route: withInstallationIdOnReplyRoute(args.delivery_capabilities.default_reply_route, state.identity?.installation_id || null),
|
|
1297
|
-
}
|
|
1298
|
-
: normalizeDeliveryCapabilitiesWithInstallationId(args.delivery_capabilities, state.identity?.installation_id || null)) }
|
|
1393
|
+
// 但 reply route 里的 session_key 仍然必须先过 canonical 校验。
|
|
1394
|
+
? { delivery_capabilities: normalizeDeliveryCapabilitiesWithInstallationId(args.delivery_capabilities, state.identity?.installation_id || null) }
|
|
1299
1395
|
: {}),
|
|
1300
1396
|
};
|
|
1301
1397
|
const updated = await gateway.updateInstallation(updateRequest);
|
|
@@ -1527,7 +1623,7 @@ async function listTools() {
|
|
|
1527
1623
|
function createMcpServer() {
|
|
1528
1624
|
const server = new Server({
|
|
1529
1625
|
name: 'hi-mcp-server',
|
|
1530
|
-
version: '0.1.
|
|
1626
|
+
version: '0.1.7',
|
|
1531
1627
|
}, {
|
|
1532
1628
|
capabilities: {
|
|
1533
1629
|
tools: {},
|