惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
美团技术团队
Last Week in AI
Last Week in AI
WordPress大学
WordPress大学
博客园 - 三生石上(FineUI控件)
博客园 - 聂微东
雷峰网
雷峰网
阮一峰的网络日志
阮一峰的网络日志
博客园 - 叶小钗
IT之家
IT之家
Google DeepMind News
Google DeepMind News
D
Docker
J
Java Code Geeks
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 【当耐特】
V
V2EX
Hugging Face - Blog
Hugging Face - Blog
博客园 - Franky
月光博客
月光博客
宝玉的分享
宝玉的分享
酷 壳 – CoolShell
酷 壳 – CoolShell
aimingoo的专栏
aimingoo的专栏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
fix(imessage): preserve SMS approval reply routes · openc...
kevinslin · 2026-05-30 · via Recent Commits to openclaw:main
Original file line numberDiff line numberDiff line change

@@ -142,6 +142,14 @@ function resolveIMessageOutboundSessionRoute(params: {

142142

if (!handle) {

143143

return null;

144144

}

145+

const account = resolveIMessageAccount({ cfg: params.cfg, accountId: params.accountId });

146+

const service =

147+

parsed.serviceExplicit || parsed.service !== "auto"

148+

? parsed.service

149+

: account.config.service === "sms"

150+

? "sms"

151+

: "imessage";

152+

const directTarget = `${service}:${handle}`;

145153

const peer: RoutePeer = { kind: "direct", id: handle };

146154

const baseSessionKey = buildIMessageBaseSessionKey({

147155

cfg: params.cfg,

@@ -154,8 +162,8 @@ function resolveIMessageOutboundSessionRoute(params: {

154162

baseSessionKey,

155163

peer,

156164

chatType: "direct" as const,

157-

from: `imessage:${handle}`,

158-

to: `imessage:${handle}`,

165+

from: directTarget,

166+

to: directTarget,

159167

};

160168

}

161169
Original file line numberDiff line numberDiff line change

@@ -29,6 +29,7 @@ import { resolveAgentRoute } from "openclaw/plugin-sdk/routing";

2929

import { uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";

3030

import { sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";

3131

import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";

32+

import { resolveIMessageAccount } from "../accounts.js";

3233

import { resolveIMessageConversationRoute } from "../conversation-route.js";

3334

import {

3435

isKnownFromMeIMessageMessageId,

@@ -902,7 +903,17 @@ export async function buildIMessageInboundContext(params: {

902903

});

903904

}

904905
905-

const imessageTo = (decision.isGroup ? chatTarget : undefined) || `imessage:${decision.sender}`;

906+

const imessageTo = decision.isGroup

907+

? chatTarget || `imessage:${decision.sender}`

908+

: buildDirectIMessageReplyTarget({

909+

cfg: params.cfg,

910+

accountId: decision.route.accountId,

911+

sender: decision.sender,

912+

});

913+

// Async follow-ups can resume from the stored origin instead of the immediate

914+

// reply target. Keep direct SMS origins service-qualified the same way as To,

915+

// or the final resumed message can fall back to imessage:<phone>.

916+

const imessageFrom = decision.isGroup ? `imessage:group:${chatId ?? "unknown"}` : imessageTo;

906917

const inboundHistory =

907918

!decision.isGroup && params.dmHistory?.inboundHistory

908919

? params.dmHistory.inboundHistory

@@ -940,9 +951,7 @@ export async function buildIMessageInboundContext(params: {

940951

messageId: messageSid,

941952

messageIdFull: messageGuid,

942953

timestamp: decision.createdAt,

943-

from: decision.isGroup

944-

? `imessage:group:${chatId ?? "unknown"}`

945-

: `imessage:${decision.sender}`,

954+

from: imessageFrom,

946955

sender: {

947956

id: decision.sender,

948957

name: decision.senderNormalized,

@@ -1023,6 +1032,19 @@ function buildIMessageEchoScope(params: {

10231032

return scopes;

10241033

}

10251034
1035+

function buildDirectIMessageReplyTarget(params: {

1036+

cfg: OpenClawConfig;

1037+

accountId?: string | null;

1038+

sender: string;

1039+

}): string {

1040+

const account = resolveIMessageAccount({ cfg: params.cfg, accountId: params.accountId });

1041+

const configuredService = account.config.service;

1042+

if (configuredService === "sms") {

1043+

return `sms:${params.sender}`;

1044+

}

1045+

return `imessage:${params.sender}`;

1046+

}

1047+
10261048

export function describeIMessageEchoDropLog(params: {

10271049

messageText: string;

10281050

messageId?: string;

Original file line numberDiff line numberDiff line change

@@ -654,7 +654,7 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P

654654

logVerbose,

655655

})

656656

: undefined;

657-

const { ctxPayload, chatTarget } = await buildIMessageInboundContext({

657+

const { ctxPayload, chatTarget, imessageTo } = await buildIMessageInboundContext({

658658

cfg,

659659

decision,

660660

message,

@@ -671,7 +671,7 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P

671671

},

672672

});

673673
674-

const updateTarget = chatTarget || decision.sender;

674+

const updateTarget = chatTarget || imessageTo;

675675

const pinnedMainDmOwner = resolvePinnedMainDmOwnerFromAllowlist({

676676

dmScope: cfg.session?.dmScope,

677677

allowFrom,

Original file line numberDiff line numberDiff line change

@@ -236,6 +236,16 @@ function isNumericMessageRowId(value: string | null | undefined): value is strin

236236

return typeof value === "string" && /^\d+$/.test(value.trim());

237237

}

238238
239+

function resolveTargetService(target: ParsedIMessageTarget): IMessageService | undefined {

240+

if (target.kind !== "handle") {

241+

return undefined;

242+

}

243+

if (target.serviceExplicit || target.service !== "auto") {

244+

return target.service;

245+

}

246+

return undefined;

247+

}

248+
239249

function normalizeResolvedMessageGuid(value: unknown): string | null {

240250

if (typeof value !== "string") {

241251

return null;

@@ -799,8 +809,9 @@ export async function sendMessageIMessage(

799809

const target = parseIMessageTarget(opts.chatId ? formatIMessageChatTarget(opts.chatId) : to);

800810

const service =

801811

opts.service ??

802-

(target.kind === "handle" ? target.service : undefined) ??

812+

resolveTargetService(target) ??

803813

(account.config.service as IMessageService | undefined);

814+

const timeoutMs = opts.timeoutMs ?? account.config.probeTimeoutMs;

804815

const region = opts.region?.trim() || account.config.region?.trim() || "US";

805816

const maxBytes =

806817

typeof opts.maxBytes === "number"

@@ -852,7 +863,7 @@ export async function sendMessageIMessage(

852863

const resolvedReplyToId = sanitizeReplyToId(opts.replyToId);

853864

const runCliJson =

854865

opts.runCliJson ??

855-

((args: readonly string[]) => runIMessageCliJson(cliPath, dbPath, args, opts.timeoutMs));

866+

((args: readonly string[]) => runIMessageCliJson(cliPath, dbPath, args, timeoutMs));

856867
857868

if (filePath && !message.trim() && !resolvedReplyToId) {

858869

const attachmentResult = await trySendAttachmentForExplicitChat({

@@ -904,7 +915,7 @@ export async function sendMessageIMessage(

904915

try {

905916

try {

906917

result = await client.request<Record<string, unknown>>("send", params, {

907-

timeoutMs: opts.timeoutMs,

918+

timeoutMs,

908919

});

909920

} catch (error) {

910921

if (filePath || resolvedReplyToId || !isIMessageRpcSendTimeout(error)) {

Original file line numberDiff line numberDiff line change

@@ -15,7 +15,7 @@ export type IMessageTarget =

1515

| { kind: "chat_id"; chatId: number }

1616

| { kind: "chat_guid"; chatGuid: string }

1717

| { kind: "chat_identifier"; chatIdentifier: string }

18-

| { kind: "handle"; to: string; service: IMessageService };

18+

| { kind: "handle"; to: string; service: IMessageService; serviceExplicit?: boolean };

1919
2020

export type IMessageAllowTarget = ParsedChatTarget | { kind: "handle"; handle: string };

2121

@@ -91,6 +91,9 @@ export function parseIMessageTarget(raw: string): IMessageTarget {

9191

parseTarget: parseIMessageTarget,

9292

});

9393

if (servicePrefixed) {

94+

if (servicePrefixed.kind === "handle") {

95+

return { ...servicePrefixed, serviceExplicit: true };

96+

}

9497

return servicePrefixed;

9598

}

9699