mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-08 22:00:50 +08:00
Make WhatsApp member presets follow channel mapping by default.
Some checks failed
CI / Test, build, and verify package (push) Has been cancelled
Some checks failed
CI / Test, build, and verify package (push) Has been cancelled
Empty member/group Agent Preset now resolves to the bot workspace mapping (not Host global); settings label is Follow channel mapping, and /new createSession carries that mapping explicitly. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
f1d85d2985
commit
bcbe85fdb1
10 changed files with 182 additions and 131 deletions
|
|
@ -8,6 +8,9 @@ This file records the notable changes in each dsh-im release. Its format follows
|
|||
|
||||
### Fixed / 修复
|
||||
|
||||
- WhatsApp 成员/群 Agent Preset 空值(UI 原「跟随全局」)现在明确回落到**频道工作区映射预设**,不再让人误以为跟 Host 全局默认;设置页文案改为「跟随频道映射」。`/new` 后建会话也会带上该映射,避免改 Host 全局预设后误用坏默认导致 `SESSION_CREATE`。
|
||||
Empty WhatsApp member/group Agent Preset (UI formerly “Follow global”) now clearly falls back to the **channel workspace mapping preset**, not the Host catalog default; settings copy is “Follow channel mapping”. `/new` session creation also carries that mapping so a broken Host global default after operators change it does not surface as `SESSION_CREATE`.
|
||||
|
||||
- Host 带斜杠的错误码(如 `gateway/internal`、`workspace/invalid-path`、`agent-preset/not-found`)现在会正确映射为 `SESSION_CREATE` / `WORKSPACE_UNAVAILABLE` / `PRESET_UNAVAILABLE`;`last-message-failure.json` 额外写入 workspace、Host details 与脱敏 message,便于现场对上真实 RPC。
|
||||
Host slash-branded failure codes (e.g. `gateway/internal`, `workspace/invalid-path`, `agent-preset/not-found`) now map to `SESSION_CREATE` / `WORKSPACE_UNAVAILABLE` / `PRESET_UNAVAILABLE`; `last-message-failure.json` also records workspace, Host details, and redacted messages for field triage.
|
||||
|
||||
|
|
|
|||
|
|
@ -571,7 +571,7 @@ var React26 = __toESM(require("react"), 1);
|
|||
// package.json
|
||||
var package_default = {
|
||||
name: "dsh-im-ops",
|
||||
version: "4.9.1-ops.35",
|
||||
version: "4.9.1-ops.36",
|
||||
description: "Ops fork of dsh-im (all channels kept). Based on @xmanrui/dsh-im@4.9.1 - access/session policies owned here.",
|
||||
keywords: [
|
||||
"deepseek-harness",
|
||||
|
|
@ -855,6 +855,7 @@ var EN = Object.freeze({
|
|||
"\u6E20\u9053\u7528\u6237": "Channel user",
|
||||
"\uFF08\u4E0D\u53EF\u7528\uFF09": " (unavailable)",
|
||||
"\u7FA4 Agent Preset": "Group Agent Preset",
|
||||
"\u8DDF\u968F\u9891\u9053\u6620\u5C04": "Follow channel mapping",
|
||||
"\u8DDF\u968F\u5168\u5C40": "Follow global",
|
||||
"\u2014": "\u2014",
|
||||
"\u6765\u6E90": "Source",
|
||||
|
|
@ -13036,7 +13037,7 @@ function AgentPresetSelect({
|
|||
"aria-label": ariaLabel,
|
||||
onChange: (event) => onChange(event.target.value || "")
|
||||
},
|
||||
h2("option", { value: "" }, "\u8DDF\u968F\u5168\u5C40"),
|
||||
h2("option", { value: "" }, localizeText("\u8DDF\u968F\u9891\u9053\u6620\u5C04")),
|
||||
...items.map((item) => h2("option", {
|
||||
key: item.id,
|
||||
value: item.id
|
||||
|
|
|
|||
234
lib/index.js
234
lib/index.js
File diff suppressed because one or more lines are too long
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "dsh-im-ops",
|
||||
"version": "4.9.1-ops.35",
|
||||
"version": "4.9.1-ops.36",
|
||||
"description": "Ops fork of dsh-im (all channels kept). Based on @xmanrui/dsh-im@4.9.1 - access/session policies owned here.",
|
||||
"keywords": [
|
||||
"deepseek-harness",
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ function AgentPresetSelect({
|
|||
'aria-label': ariaLabel,
|
||||
onChange: (event) => onChange(event.target.value || ''),
|
||||
},
|
||||
h('option', { value: '' }, '跟随全局'),
|
||||
h('option', { value: '' }, localizeText('跟随频道映射')),
|
||||
...items.map((item) => h('option', {
|
||||
key: item.id,
|
||||
value: item.id,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ const EN = Object.freeze({
|
|||
'渠道用户': 'Channel user',
|
||||
"(不可用)": " (unavailable)",
|
||||
"群 Agent Preset": "Group Agent Preset",
|
||||
"跟随频道映射": "Follow channel mapping",
|
||||
"跟随全局": "Follow global",
|
||||
"—": "—",
|
||||
"来源": "Source",
|
||||
|
|
|
|||
|
|
@ -310,8 +310,9 @@ export function normalizeAccessGrant(input) {
|
|||
|
||||
/**
|
||||
* Resolve a chat-scoped Agent Preset override from the access grant.
|
||||
* Returns null when the chat should follow the bot-level (global) preset.
|
||||
* Priority: direct-member or group override > bot global (caller applies global).
|
||||
* Returns null when the chat should follow the bot/channel mapping preset
|
||||
* (workspaces.agentPresets[botId]), not the Host catalog default.
|
||||
* Priority: direct-member or group override > bot mapping (caller applies).
|
||||
* @param {unknown} grant
|
||||
* @param {{ kind?: string, phone?: string, senderId?: string, groupJid?: string, conversationId?: string }} [context]
|
||||
* @returns {string|null}
|
||||
|
|
@ -332,6 +333,25 @@ export function resolveAccessAgentPreset(grant, context = {}) {
|
|||
return normalizeOptionalAgentPresetId(member?.agentPreset) ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Member/group override, else the bot's workspace-mapped preset.
|
||||
* Does not fall through to Host catalog default — that is only used when the
|
||||
* bot mapping itself is empty (createSession omits agentPreset).
|
||||
* @param {unknown} grant
|
||||
* @param {{ kind?: string, phone?: string, senderId?: string, groupJid?: string, conversationId?: string }} [context]
|
||||
* @param {string|null|undefined} botAgentPreset
|
||||
* @returns {string|null}
|
||||
*/
|
||||
export function resolveChatAgentPreset(grant, context = {}, botAgentPreset = null) {
|
||||
const fromGrant = resolveAccessAgentPreset(grant, context);
|
||||
if (fromGrant) return fromGrant;
|
||||
if (typeof botAgentPreset === 'string') {
|
||||
const trimmed = botAgentPreset.trim();
|
||||
if (trimmed) return trimmed;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seed owner phone as the first global admin when missing.
|
||||
* @param {object|null} grant
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { normalizeAccessPhone, phoneFromWhatsappJid, resolveAccessAgentPreset } from './access-grant.mjs';
|
||||
import { normalizeAccessPhone, phoneFromWhatsappJid, resolveChatAgentPreset } from './access-grant.mjs';
|
||||
|
||||
/**
|
||||
* Parse a durable conversation binding key into chat identity fields.
|
||||
|
|
@ -118,14 +118,13 @@ export function resolveChannelPeerFromBinding(input) {
|
|||
const botAgentPreset = typeof input.botAgentPreset === 'string' ? input.botAgentPreset.trim() : '';
|
||||
|
||||
const withPreset = (peer) => {
|
||||
const fromGrant = resolveAccessAgentPreset(grant, {
|
||||
const agentPreset = resolveChatAgentPreset(grant, {
|
||||
kind: peer.kind === 'group' ? 'group' : 'direct',
|
||||
groupJid: peer.kind === 'group' ? peer.conversationId : undefined,
|
||||
conversationId: peer.conversationId,
|
||||
phone: peer.phone,
|
||||
senderId: peer.senderId || peer.phone,
|
||||
});
|
||||
const agentPreset = fromGrant || botAgentPreset || null;
|
||||
}, botAgentPreset);
|
||||
return Object.freeze({ ...peer, agentPreset });
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
normalizeMessageContent,
|
||||
} from '@whiskeysockets/baileys';
|
||||
|
||||
import { emptyAccessGrant, ensureGroupBucket, normalizeAccessPhone, phoneFromWhatsappJid, resolveAccessAgentPreset } from '../shared/access-grant.mjs';
|
||||
import { emptyAccessGrant, ensureGroupBucket, normalizeAccessPhone, phoneFromWhatsappJid, resolveChatAgentPreset } from '../shared/access-grant.mjs';
|
||||
import { splitMessageText } from '../shared/editable-message-stream.mjs';
|
||||
import { t } from '../shared/i18n.mjs';
|
||||
import { ImagePromptError } from '../shared/image-prompt.mjs';
|
||||
|
|
@ -833,7 +833,8 @@ export class WhatsappRuntime {
|
|||
resolveAgentPreset: (message) => {
|
||||
if (!this.#workspaces || !this.#botId) return null;
|
||||
const grant = this.#workspaces.accessGrantFor(this.#botId);
|
||||
return resolveAccessAgentPreset(grant, {
|
||||
// Member/group override, else WhatsApp workspace mapping — never Host global.
|
||||
return resolveChatAgentPreset(grant, {
|
||||
kind: message?.kind === 'group' ? 'group' : 'direct',
|
||||
groupJid: message?.conversationId,
|
||||
conversationId: message?.conversationId,
|
||||
|
|
@ -842,7 +843,7 @@ export class WhatsappRuntime {
|
|||
?? phoneFromWhatsappJid(message?.senderAlternateId)
|
||||
?? normalizeAccessPhone(message?.senderAlternateId),
|
||||
senderId: message?.senderId,
|
||||
});
|
||||
}, this.#workspaces.agentPresetFor(this.#botId));
|
||||
},
|
||||
});
|
||||
const now = Date.now();
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import {
|
|||
resolveAccessPending,
|
||||
upsertAccessContact,
|
||||
resolveAccessAgentPreset,
|
||||
resolveChatAgentPreset,
|
||||
validateAccessGrant,
|
||||
} from '../../../src/channels/shared/access-grant.mjs';
|
||||
|
||||
|
|
@ -233,7 +234,7 @@ test('quote approval helpers parse intent, pending id, and notify refs', () => {
|
|||
});
|
||||
|
||||
|
||||
test('resolveAccessAgentPreset prefers direct-member and group overrides over global', () => {
|
||||
test('resolveAccessAgentPreset prefers direct-member and group overrides over channel mapping', () => {
|
||||
const doc = grant({
|
||||
directMembers: [
|
||||
{ phone: '8618222222222', canExecuteCommands: true, agentPreset: 'ops-user' },
|
||||
|
|
@ -266,6 +267,31 @@ test('resolveAccessAgentPreset prefers direct-member and group overrides over gl
|
|||
}), null);
|
||||
});
|
||||
|
||||
test('resolveChatAgentPreset falls back to bot/channel mapping, not Host global', () => {
|
||||
const doc = grant({
|
||||
directMembers: [
|
||||
{ phone: '8618222222222', canExecuteCommands: true, agentPreset: 'ops-user' },
|
||||
{ phone: '8618333333333', canExecuteCommands: true },
|
||||
],
|
||||
});
|
||||
assert.equal(resolveChatAgentPreset(doc, {
|
||||
kind: 'direct',
|
||||
phone: '8618222222222',
|
||||
}, 'wa-mapped'), 'ops-user');
|
||||
assert.equal(resolveChatAgentPreset(doc, {
|
||||
kind: 'direct',
|
||||
phone: '8618333333333',
|
||||
}, 'wa-mapped'), 'wa-mapped');
|
||||
assert.equal(resolveChatAgentPreset(doc, {
|
||||
kind: 'direct',
|
||||
phone: '8618333333333',
|
||||
}, null), null);
|
||||
assert.equal(resolveChatAgentPreset(null, {
|
||||
kind: 'direct',
|
||||
phone: '8618333333333',
|
||||
}, 'wa-mapped'), 'wa-mapped');
|
||||
});
|
||||
|
||||
test('validateAccessGrant keeps optional agentPreset on members and groups', () => {
|
||||
const doc = grant({
|
||||
directMembers: [{ phone: '8618222222222', canExecuteCommands: false, agentPreset: 'desk' }],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue