mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
refactor: share native approval route gates
Share native approval route gate helpers across mainstream channel approval runtimes and keep PR #87770 green on current main.
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
74c6b635c822358e61473a5b1bf7e6786592c459289057db2fddf807ffa022ec plugin-sdk-api-baseline.json
|
||||
4db87f9b57209632cd3887d791277f6c30c4a894b09785a2b51cb50cb5aedb78 plugin-sdk-api-baseline.jsonl
|
||||
4272d9b4edc35fbdf9e1980c109845f59283a5c1e120235fb158a9db3c054b01 plugin-sdk-api-baseline.json
|
||||
e24313a7fadaad09b58c1b683e2906973ff01d8b1bb3f403b9cef30bdcb89ee6 plugin-sdk-api-baseline.jsonl
|
||||
|
||||
@@ -155,6 +155,7 @@ Most channel plugins do not need approval-specific code.
|
||||
- If custom approval auth intentionally allows only same-chat fallback, return `markImplicitSameChatApprovalAuthorization({ authorized: true })` from `openclaw/plugin-sdk/approval-auth-runtime`; otherwise core treats the result as explicit approver authorization.
|
||||
- If a channel-owned native callback resolves approvals directly, use `isImplicitSameChatApprovalAuthorization(...)` before resolving so implicit fallback still goes through the channel's normal actor authorization.
|
||||
- If a channel needs native approval delivery, keep channel code focused on target normalization plus transport/presentation facts. Use `createChannelExecApprovalProfile`, `createChannelNativeOriginTargetResolver`, `createChannelApproverDmTargetResolver`, and `createApproverRestrictedNativeApprovalCapability` from `openclaw/plugin-sdk/approval-runtime`. Put the channel-specific facts behind `approvalCapability.nativeRuntime`, ideally via `createChannelApprovalNativeRuntimeAdapter(...)` or `createLazyChannelApprovalNativeRuntimeAdapter(...)`, so core can assemble the handler and own request filtering, routing, dedupe, expiry, gateway subscription, and routed-elsewhere notices. `nativeRuntime` is split into a few smaller seams:
|
||||
- Use `createNativeApprovalChannelRouteGates` from `openclaw/plugin-sdk/approval-native-runtime` when a channel supports both session-origin native delivery and explicit approval forwarding targets. The helper centralizes approval config selection, `mode` handling, agent/session filters, account binding, session-target matching, and target-list matching while callers still own the channel id, default forwarding mode, account lookup, transport-enabled check, target normalization, and turn-source target resolution. Do not use it to create core-owned channel policy defaults; pass the channel's documented default mode explicitly.
|
||||
- `createChannelNativeOriginTargetResolver` uses the shared channel-route matcher by default for `{ to, accountId, threadId }` targets. Pass `targetsMatch` only when a channel has provider-specific equivalence rules, such as Slack timestamp prefix matching.
|
||||
- Pass `normalizeTargetForMatch` to `createChannelNativeOriginTargetResolver` when the channel needs to canonicalize provider ids before the default route matcher or a custom `targetsMatch` callback runs, while preserving the original target for delivery. Use `normalizeTarget` only when the resolved delivery target itself should be canonicalized.
|
||||
- `availability` - whether the account is configured and whether a request should be handled
|
||||
|
||||
@@ -179,7 +179,7 @@ and pairing-path families.
|
||||
| `plugin-sdk/approval-gateway-runtime` | Shared approval gateway-resolution helper |
|
||||
| `plugin-sdk/approval-handler-adapter-runtime` | Lightweight native approval adapter loading helpers for hot channel entrypoints |
|
||||
| `plugin-sdk/approval-handler-runtime` | Broader approval handler runtime helpers; prefer the narrower adapter/gateway seams when they are enough |
|
||||
| `plugin-sdk/approval-native-runtime` | Native approval target + account-binding helpers and local native exec prompt suppression |
|
||||
| `plugin-sdk/approval-native-runtime` | Native approval target, account-binding, route-gate, forwarding fallback, and local native exec prompt suppression helpers |
|
||||
| `plugin-sdk/approval-reaction-runtime` | Hardcoded approval reaction bindings, reaction prompt payloads, reaction target stores, and compatibility export for local native exec prompt suppression |
|
||||
| `plugin-sdk/approval-reply-runtime` | Exec/plugin approval reply payload helpers |
|
||||
| `plugin-sdk/approval-runtime` | Exec/plugin approval payload helpers, native approval routing/runtime helpers, and structured approval display helpers such as `formatApprovalDisplayPath` |
|
||||
|
||||
@@ -79,6 +79,18 @@ function buildConversationKeyForTarget(to: string): IMessageApprovalConversation
|
||||
}
|
||||
}
|
||||
|
||||
function shouldThreadApprovalUpdate(to: string): boolean {
|
||||
try {
|
||||
const parsed = parseIMessageTarget(to);
|
||||
if (parsed.kind === "handle" && parsed.service === "sms") {
|
||||
return false;
|
||||
}
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export const imessageApprovalNativeRuntime = createChannelApprovalNativeRuntimeAdapter<
|
||||
IMessagePendingDelivery,
|
||||
PreparedIMessageApprovalTarget,
|
||||
@@ -151,7 +163,7 @@ export const imessageApprovalNativeRuntime = createChannelApprovalNativeRuntimeA
|
||||
await sendMessageIMessage(entry.to, payload.text, {
|
||||
config: cfg,
|
||||
...(entry.accountId ? { accountId: entry.accountId } : {}),
|
||||
replyToId: entry.messageId,
|
||||
...(shouldThreadApprovalUpdate(entry.to) ? { replyToId: entry.messageId } : {}),
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
@@ -5,12 +5,10 @@ import {
|
||||
import { createLazyChannelApprovalNativeRuntimeAdapter } from "openclaw/plugin-sdk/approval-handler-adapter-runtime";
|
||||
import type { ChannelApprovalNativeRuntimeAdapter } from "openclaw/plugin-sdk/approval-handler-runtime";
|
||||
import {
|
||||
createChannelApprovalForwardingEvaluator,
|
||||
createChannelApproverDmTargetResolver,
|
||||
createChannelNativeOriginTargetResolver,
|
||||
createNativeApprovalChannelRouteGates,
|
||||
createNativeApprovalForwardingFallbackSuppressor,
|
||||
nativeApprovalTargetsMatch,
|
||||
resolveApprovalRequestSessionTarget,
|
||||
shouldSuppressLocalNativeExecApprovalPrompt,
|
||||
} from "openclaw/plugin-sdk/approval-native-runtime";
|
||||
import {
|
||||
@@ -48,8 +46,8 @@ import { normalizeIMessageMessagingTarget } from "./normalize.js";
|
||||
import { inferIMessageTargetChatType } from "./targets.js";
|
||||
|
||||
type ApprovalRequest = ExecApprovalRequest | PluginApprovalRequest;
|
||||
type ApprovalKind = "exec" | "plugin";
|
||||
type ApprovalForwardingConfig = NonNullable<NonNullable<OpenClawConfig["approvals"]>["exec"]>;
|
||||
type ApprovalForwardingMode = NonNullable<ApprovalForwardingConfig["mode"]>;
|
||||
type ChannelApprovalForwardTarget = Parameters<
|
||||
NonNullable<
|
||||
NonNullable<ChannelApprovalCapability["delivery"]>["shouldSuppressForwardingFallback"]
|
||||
@@ -61,6 +59,7 @@ type IMessageApprovalTarget = {
|
||||
threadId?: string | number | null;
|
||||
};
|
||||
|
||||
const DEFAULT_APPROVAL_FORWARDING_MODE: ApprovalForwardingMode = "session";
|
||||
const DEFAULT_PLUGIN_APPROVAL_DECISIONS: readonly ExecApprovalReplyDecision[] = [
|
||||
"allow-once",
|
||||
"allow-always",
|
||||
@@ -74,35 +73,6 @@ function isIMessageApprovalTransportEnabled(params: {
|
||||
return resolveIMessageAccount({ cfg: params.cfg, accountId: params.accountId }).enabled;
|
||||
}
|
||||
|
||||
function targetAccountMatchesIMessageAccount(params: {
|
||||
cfg: OpenClawConfig;
|
||||
targetAccountId?: string | null;
|
||||
accountId?: string | null;
|
||||
}): boolean {
|
||||
const targetAccountId = normalizeOptionalString(params.targetAccountId);
|
||||
const accountId = normalizeOptionalString(params.accountId);
|
||||
if (targetAccountId) {
|
||||
return !accountId || normalizeAccountId(targetAccountId) === normalizeAccountId(accountId);
|
||||
}
|
||||
if (!accountId) {
|
||||
return true;
|
||||
}
|
||||
const normalizedAccountId = normalizeAccountId(accountId);
|
||||
const defaultAccountId = normalizeAccountId(resolveDefaultIMessageAccountId(params.cfg));
|
||||
if (normalizedAccountId === defaultAccountId) {
|
||||
return true;
|
||||
}
|
||||
const enabledAccountIds = listIMessageAccountIds(params.cfg)
|
||||
.filter((candidateAccountId) =>
|
||||
isIMessageApprovalTransportEnabled({
|
||||
cfg: params.cfg,
|
||||
accountId: candidateAccountId,
|
||||
}),
|
||||
)
|
||||
.map((candidateAccountId) => normalizeAccountId(candidateAccountId));
|
||||
return enabledAccountIds.length === 1 && enabledAccountIds[0] === normalizedAccountId;
|
||||
}
|
||||
|
||||
function normalizeIMessageForwardTarget(
|
||||
target: Pick<ChannelApprovalForwardTarget, "channel" | "to" | "accountId" | "threadId">,
|
||||
): IMessageApprovalTarget | null {
|
||||
@@ -120,73 +90,6 @@ function normalizeIMessageForwardTarget(
|
||||
};
|
||||
}
|
||||
|
||||
function hasMatchingIMessageTarget(params: {
|
||||
cfg: OpenClawConfig;
|
||||
config: ApprovalForwardingConfig;
|
||||
accountId?: string | null;
|
||||
target?: ChannelApprovalForwardTarget;
|
||||
}): boolean {
|
||||
const candidateTarget = params.target ? normalizeIMessageForwardTarget(params.target) : null;
|
||||
return (params.config.targets ?? []).some((target) => {
|
||||
const configuredTarget = normalizeIMessageForwardTarget(target);
|
||||
if (!configuredTarget) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
!targetAccountMatchesIMessageAccount({
|
||||
cfg: params.cfg,
|
||||
targetAccountId: configuredTarget.accountId,
|
||||
accountId: params.accountId,
|
||||
})
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (!candidateTarget) {
|
||||
return true;
|
||||
}
|
||||
return nativeApprovalTargetsMatch({
|
||||
channel: "imessage",
|
||||
left: configuredTarget,
|
||||
right: candidateTarget,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function hasIMessageOriginOrSessionTarget(params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
request: ApprovalRequest;
|
||||
}): boolean {
|
||||
if (resolveTurnSourceIMessageOriginTarget(params.request)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const sessionTarget = resolveApprovalRequestSessionTarget({
|
||||
cfg: params.cfg,
|
||||
request: params.request,
|
||||
});
|
||||
return (
|
||||
normalizeLowercaseStringOrEmpty(sessionTarget?.channel) === "imessage" &&
|
||||
targetAccountMatchesIMessageAccount({
|
||||
cfg: params.cfg,
|
||||
targetAccountId: sessionTarget?.accountId,
|
||||
accountId: params.accountId,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const imessageApprovalForwarding = createChannelApprovalForwardingEvaluator({
|
||||
channel: "imessage",
|
||||
isTransportEnabled: isIMessageApprovalTransportEnabled,
|
||||
hasMatchingTarget: hasMatchingIMessageTarget,
|
||||
hasOriginOrSessionTarget: hasIMessageOriginOrSessionTarget,
|
||||
});
|
||||
|
||||
const canApprovalPotentiallyRouteToIMessage = imessageApprovalForwarding.isPotentialRoute;
|
||||
const canAnyApprovalPotentiallyRouteToIMessage = imessageApprovalForwarding.canAnyPotentiallyRoute;
|
||||
const isIMessageSessionApprovalEligible = imessageApprovalForwarding.isSessionEligible;
|
||||
const isIMessageExplicitTargetEligible = imessageApprovalForwarding.isExplicitTargetEligible;
|
||||
|
||||
function resolveTurnSourceIMessageOriginTarget(
|
||||
request: ApprovalRequest,
|
||||
): IMessageApprovalTarget | null {
|
||||
@@ -212,6 +115,24 @@ function resolveSessionIMessageOriginTarget(sessionTarget: {
|
||||
return to ? { to, accountId: normalizeOptionalString(sessionTarget.accountId) } : null;
|
||||
}
|
||||
|
||||
const imessageApprovalRouteGates = createNativeApprovalChannelRouteGates({
|
||||
channel: "imessage",
|
||||
defaultForwardingMode: DEFAULT_APPROVAL_FORWARDING_MODE,
|
||||
isTransportEnabled: isIMessageApprovalTransportEnabled,
|
||||
listAccountIds: listIMessageAccountIds,
|
||||
resolveDefaultAccountId: resolveDefaultIMessageAccountId,
|
||||
normalizeForwardTarget: normalizeIMessageForwardTarget,
|
||||
resolveTurnSourceTarget: resolveTurnSourceIMessageOriginTarget,
|
||||
});
|
||||
|
||||
const {
|
||||
canApprovalPotentiallyRouteToChannel: canApprovalPotentiallyRouteToIMessage,
|
||||
canAnyApprovalPotentiallyRouteToChannel: canAnyApprovalPotentiallyRouteToIMessage,
|
||||
isSessionApprovalEligible: isIMessageSessionApprovalEligible,
|
||||
isExplicitTargetEligible: isIMessageExplicitTargetEligible,
|
||||
shouldHandleApprovalRequest: shouldHandleIMessageApprovalRequest,
|
||||
} = imessageApprovalRouteGates;
|
||||
|
||||
function resolveIMessageSessionTargetFromSessionKey(
|
||||
sessionKey?: string | null,
|
||||
): IMessageApprovalTarget | null {
|
||||
@@ -308,15 +229,6 @@ export function shouldSuppressLocalIMessageExecApprovalPrompt(params: {
|
||||
});
|
||||
}
|
||||
|
||||
function shouldHandleIMessageApprovalRequest(params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
approvalKind?: ApprovalKind;
|
||||
request: ApprovalRequest;
|
||||
}): boolean {
|
||||
return imessageApprovalForwarding.shouldHandleRequest(params);
|
||||
}
|
||||
|
||||
const resolveIMessageOriginTargetBase = createChannelNativeOriginTargetResolver({
|
||||
channel: "imessage",
|
||||
shouldHandleRequest: shouldHandleIMessageApprovalRequest,
|
||||
|
||||
@@ -5,12 +5,10 @@ import {
|
||||
import { createLazyChannelApprovalNativeRuntimeAdapter } from "openclaw/plugin-sdk/approval-handler-adapter-runtime";
|
||||
import type { ChannelApprovalNativeRuntimeAdapter } from "openclaw/plugin-sdk/approval-handler-runtime";
|
||||
import {
|
||||
createChannelApprovalForwardingEvaluator,
|
||||
createChannelApproverDmTargetResolver,
|
||||
createChannelNativeOriginTargetResolver,
|
||||
createNativeApprovalChannelRouteGates,
|
||||
createNativeApprovalForwardingFallbackSuppressor,
|
||||
nativeApprovalTargetsMatch,
|
||||
resolveApprovalRequestSessionTarget,
|
||||
shouldSuppressLocalNativeExecApprovalPrompt,
|
||||
} from "openclaw/plugin-sdk/approval-native-runtime";
|
||||
import { buildApprovalReactionPendingContentForRequest } from "openclaw/plugin-sdk/approval-reaction-runtime";
|
||||
@@ -24,7 +22,7 @@ import type {
|
||||
} from "openclaw/plugin-sdk/channel-contract";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||
import type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
|
||||
import { normalizeAccountId, parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
|
||||
import { parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalString,
|
||||
@@ -40,6 +38,7 @@ import { normalizeSignalMessagingTarget } from "./normalize.js";
|
||||
type ApprovalRequest = ExecApprovalRequest | PluginApprovalRequest;
|
||||
type ApprovalKind = "exec" | "plugin";
|
||||
type ApprovalForwardingConfig = NonNullable<NonNullable<OpenClawConfig["approvals"]>["exec"]>;
|
||||
type ApprovalForwardingMode = NonNullable<ApprovalForwardingConfig["mode"]>;
|
||||
type ChannelApprovalForwardTarget = Parameters<
|
||||
NonNullable<
|
||||
NonNullable<ChannelApprovalCapability["delivery"]>["shouldSuppressForwardingFallback"]
|
||||
@@ -51,6 +50,8 @@ type SignalApprovalTarget = {
|
||||
threadId?: string | number | null;
|
||||
};
|
||||
|
||||
const DEFAULT_APPROVAL_FORWARDING_MODE: ApprovalForwardingMode = "session";
|
||||
|
||||
function isSignalApprovalTransportEnabled(params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
@@ -58,35 +59,6 @@ function isSignalApprovalTransportEnabled(params: {
|
||||
return resolveSignalAccount({ cfg: params.cfg, accountId: params.accountId }).enabled;
|
||||
}
|
||||
|
||||
function targetAccountMatchesSignalAccount(params: {
|
||||
cfg: OpenClawConfig;
|
||||
targetAccountId?: string | null;
|
||||
accountId?: string | null;
|
||||
}): boolean {
|
||||
const targetAccountId = normalizeOptionalString(params.targetAccountId);
|
||||
const accountId = normalizeOptionalString(params.accountId);
|
||||
if (targetAccountId) {
|
||||
return !accountId || normalizeAccountId(targetAccountId) === normalizeAccountId(accountId);
|
||||
}
|
||||
if (!accountId) {
|
||||
return true;
|
||||
}
|
||||
const normalizedAccountId = normalizeAccountId(accountId);
|
||||
const defaultAccountId = normalizeAccountId(resolveDefaultSignalAccountId(params.cfg));
|
||||
if (normalizedAccountId === defaultAccountId) {
|
||||
return true;
|
||||
}
|
||||
const enabledAccountIds = listSignalAccountIds(params.cfg)
|
||||
.filter((candidateAccountId) =>
|
||||
isSignalApprovalTransportEnabled({
|
||||
cfg: params.cfg,
|
||||
accountId: candidateAccountId,
|
||||
}),
|
||||
)
|
||||
.map((candidateAccountId) => normalizeAccountId(candidateAccountId));
|
||||
return enabledAccountIds.length === 1 && enabledAccountIds[0] === normalizedAccountId;
|
||||
}
|
||||
|
||||
function normalizeSignalForwardTarget(
|
||||
target: Pick<ChannelApprovalForwardTarget, "channel" | "to" | "accountId" | "threadId">,
|
||||
): SignalApprovalTarget | null {
|
||||
@@ -104,82 +76,6 @@ function normalizeSignalForwardTarget(
|
||||
};
|
||||
}
|
||||
|
||||
function hasMatchingSignalTarget(params: {
|
||||
cfg: OpenClawConfig;
|
||||
config: ApprovalForwardingConfig;
|
||||
accountId?: string | null;
|
||||
target?: ChannelApprovalForwardTarget;
|
||||
}): boolean {
|
||||
const candidateTarget = params.target ? normalizeSignalForwardTarget(params.target) : null;
|
||||
return (params.config.targets ?? []).some((target) => {
|
||||
const configuredTarget = normalizeSignalForwardTarget(target);
|
||||
if (!configuredTarget) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
!targetAccountMatchesSignalAccount({
|
||||
cfg: params.cfg,
|
||||
targetAccountId: configuredTarget.accountId,
|
||||
accountId: params.accountId,
|
||||
})
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (!candidateTarget) {
|
||||
return true;
|
||||
}
|
||||
return nativeApprovalTargetsMatch({
|
||||
channel: "signal",
|
||||
left: configuredTarget,
|
||||
right: candidateTarget,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function hasSignalOriginOrSessionTarget(params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
request: ApprovalRequest;
|
||||
}): boolean {
|
||||
if (resolveTurnSourceSignalOriginTarget(params.request)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const sessionTarget = resolveApprovalRequestSessionTarget({
|
||||
cfg: params.cfg,
|
||||
request: params.request,
|
||||
});
|
||||
return (
|
||||
normalizeLowercaseStringOrEmpty(sessionTarget?.channel) === "signal" &&
|
||||
targetAccountMatchesSignalAccount({
|
||||
cfg: params.cfg,
|
||||
targetAccountId: sessionTarget?.accountId,
|
||||
accountId: params.accountId,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const signalApprovalForwarding = createChannelApprovalForwardingEvaluator({
|
||||
channel: "signal",
|
||||
isTransportEnabled: isSignalApprovalTransportEnabled,
|
||||
hasMatchingTarget: hasMatchingSignalTarget,
|
||||
hasOriginOrSessionTarget: hasSignalOriginOrSessionTarget,
|
||||
});
|
||||
|
||||
const canApprovalPotentiallyRouteToSignal = signalApprovalForwarding.isPotentialRoute;
|
||||
const canAnyApprovalPotentiallyRouteToSignal = signalApprovalForwarding.canAnyPotentiallyRoute;
|
||||
const isSignalSessionApprovalEligible = signalApprovalForwarding.isSessionEligible;
|
||||
|
||||
export function isSignalNativeApprovalHandlerConfigured(params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
}): boolean {
|
||||
return canAnyApprovalPotentiallyRouteToSignal({
|
||||
...params,
|
||||
nativeSessionOnly: true,
|
||||
});
|
||||
}
|
||||
|
||||
function resolveTurnSourceSignalOriginTarget(
|
||||
request: ApprovalRequest,
|
||||
): SignalApprovalTarget | null {
|
||||
@@ -205,13 +101,29 @@ function resolveSessionSignalOriginTarget(sessionTarget: {
|
||||
return to ? { to, accountId: normalizeOptionalString(sessionTarget.accountId) } : null;
|
||||
}
|
||||
|
||||
function shouldHandleSignalApprovalRequest(params: {
|
||||
const signalApprovalRouteGates = createNativeApprovalChannelRouteGates({
|
||||
channel: "signal",
|
||||
defaultForwardingMode: DEFAULT_APPROVAL_FORWARDING_MODE,
|
||||
isTransportEnabled: isSignalApprovalTransportEnabled,
|
||||
listAccountIds: listSignalAccountIds,
|
||||
resolveDefaultAccountId: resolveDefaultSignalAccountId,
|
||||
normalizeForwardTarget: normalizeSignalForwardTarget,
|
||||
resolveTurnSourceTarget: resolveTurnSourceSignalOriginTarget,
|
||||
});
|
||||
|
||||
const {
|
||||
canApprovalPotentiallyRouteToChannel: canApprovalPotentiallyRouteToSignal,
|
||||
canAnyApprovalPotentiallyRouteToChannel: canAnyApprovalPotentiallyRouteToSignal,
|
||||
isNativeApprovalHandlerConfigured: isSignalNativeApprovalHandlerConfiguredBase,
|
||||
isSessionApprovalEligible: isSignalSessionApprovalEligible,
|
||||
shouldHandleApprovalRequest: shouldHandleSignalApprovalRequest,
|
||||
} = signalApprovalRouteGates;
|
||||
|
||||
export function isSignalNativeApprovalHandlerConfigured(params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
approvalKind?: ApprovalKind;
|
||||
request: ApprovalRequest;
|
||||
}): boolean {
|
||||
return signalApprovalForwarding.shouldHandleRequest(params);
|
||||
return isSignalNativeApprovalHandlerConfiguredBase(params);
|
||||
}
|
||||
|
||||
function resolveSignalSessionTargetFromSessionKey(sessionKey?: string | null): string | null {
|
||||
|
||||
@@ -5,12 +5,10 @@ import {
|
||||
import { createLazyChannelApprovalNativeRuntimeAdapter } from "openclaw/plugin-sdk/approval-handler-adapter-runtime";
|
||||
import type { ChannelApprovalNativeRuntimeAdapter } from "openclaw/plugin-sdk/approval-handler-runtime";
|
||||
import {
|
||||
createChannelApprovalForwardingEvaluator,
|
||||
createChannelApproverDmTargetResolver,
|
||||
createChannelNativeOriginTargetResolver,
|
||||
createNativeApprovalChannelRouteGates,
|
||||
createNativeApprovalForwardingFallbackSuppressor,
|
||||
nativeApprovalTargetsMatch,
|
||||
resolveApprovalRequestSessionTarget,
|
||||
} from "openclaw/plugin-sdk/approval-native-runtime";
|
||||
import { buildApprovalReactionPromptPayloadForRequest } from "openclaw/plugin-sdk/approval-reaction-runtime";
|
||||
import type {
|
||||
@@ -19,7 +17,6 @@ import type {
|
||||
} from "openclaw/plugin-sdk/approval-runtime";
|
||||
import type { ChannelApprovalCapability } from "openclaw/plugin-sdk/channel-contract";
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||
import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalString,
|
||||
@@ -33,8 +30,8 @@ import { getWhatsAppApprovalApprovers, whatsappApprovalAuth } from "./approval-a
|
||||
import { isWhatsAppGroupJid, normalizeWhatsAppMessagingTarget } from "./normalize.js";
|
||||
|
||||
type ApprovalRequest = ExecApprovalRequest | PluginApprovalRequest;
|
||||
type ApprovalKind = "exec" | "plugin";
|
||||
type ApprovalForwardingConfig = NonNullable<NonNullable<OpenClawConfig["approvals"]>["exec"]>;
|
||||
type ApprovalForwardingMode = NonNullable<ApprovalForwardingConfig["mode"]>;
|
||||
type ChannelApprovalForwardTarget = Parameters<
|
||||
NonNullable<
|
||||
NonNullable<ChannelApprovalCapability["delivery"]>["shouldSuppressForwardingFallback"]
|
||||
@@ -46,6 +43,8 @@ type WhatsAppApprovalTarget = {
|
||||
threadId?: string | number | null;
|
||||
};
|
||||
|
||||
const DEFAULT_APPROVAL_FORWARDING_MODE: ApprovalForwardingMode = "session";
|
||||
|
||||
function isWhatsAppApprovalTransportEnabled(params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
@@ -53,35 +52,6 @@ function isWhatsAppApprovalTransportEnabled(params: {
|
||||
return resolveWhatsAppAccount({ cfg: params.cfg, accountId: params.accountId }).enabled;
|
||||
}
|
||||
|
||||
function targetAccountMatchesWhatsAppAccount(params: {
|
||||
cfg: OpenClawConfig;
|
||||
targetAccountId?: string | null;
|
||||
accountId?: string | null;
|
||||
}): boolean {
|
||||
const targetAccountId = normalizeOptionalString(params.targetAccountId);
|
||||
const accountId = normalizeOptionalString(params.accountId);
|
||||
if (targetAccountId) {
|
||||
return !accountId || normalizeAccountId(targetAccountId) === normalizeAccountId(accountId);
|
||||
}
|
||||
if (!accountId) {
|
||||
return true;
|
||||
}
|
||||
const normalizedAccountId = normalizeAccountId(accountId);
|
||||
const defaultAccountId = normalizeAccountId(resolveDefaultWhatsAppAccountId(params.cfg));
|
||||
if (normalizedAccountId === defaultAccountId) {
|
||||
return true;
|
||||
}
|
||||
const enabledAccountIds = listWhatsAppAccountIds(params.cfg)
|
||||
.filter((candidateAccountId) =>
|
||||
isWhatsAppApprovalTransportEnabled({
|
||||
cfg: params.cfg,
|
||||
accountId: candidateAccountId,
|
||||
}),
|
||||
)
|
||||
.map((candidateAccountId) => normalizeAccountId(candidateAccountId));
|
||||
return enabledAccountIds.length === 1 && enabledAccountIds[0] === normalizedAccountId;
|
||||
}
|
||||
|
||||
function normalizeWhatsAppForwardTarget(
|
||||
target: Pick<ChannelApprovalForwardTarget, "channel" | "to" | "accountId" | "threadId">,
|
||||
): WhatsAppApprovalTarget | null {
|
||||
@@ -99,73 +69,6 @@ function normalizeWhatsAppForwardTarget(
|
||||
};
|
||||
}
|
||||
|
||||
function hasMatchingWhatsAppTarget(params: {
|
||||
cfg: OpenClawConfig;
|
||||
config: ApprovalForwardingConfig;
|
||||
accountId?: string | null;
|
||||
target?: ChannelApprovalForwardTarget;
|
||||
}): boolean {
|
||||
const candidateTarget = params.target ? normalizeWhatsAppForwardTarget(params.target) : null;
|
||||
return (params.config.targets ?? []).some((target) => {
|
||||
const configuredTarget = normalizeWhatsAppForwardTarget(target);
|
||||
if (!configuredTarget) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
!targetAccountMatchesWhatsAppAccount({
|
||||
cfg: params.cfg,
|
||||
targetAccountId: configuredTarget.accountId,
|
||||
accountId: params.accountId,
|
||||
})
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (!candidateTarget) {
|
||||
return true;
|
||||
}
|
||||
return nativeApprovalTargetsMatch({
|
||||
channel: "whatsapp",
|
||||
left: configuredTarget,
|
||||
right: candidateTarget,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function hasWhatsAppOriginOrSessionTarget(params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
request: ApprovalRequest;
|
||||
}): boolean {
|
||||
if (resolveTurnSourceWhatsAppOriginTarget(params.request)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const sessionTarget = resolveApprovalRequestSessionTarget({
|
||||
cfg: params.cfg,
|
||||
request: params.request,
|
||||
});
|
||||
return (
|
||||
normalizeLowercaseStringOrEmpty(sessionTarget?.channel) === "whatsapp" &&
|
||||
targetAccountMatchesWhatsAppAccount({
|
||||
cfg: params.cfg,
|
||||
targetAccountId: sessionTarget?.accountId,
|
||||
accountId: params.accountId,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const whatsappApprovalForwarding = createChannelApprovalForwardingEvaluator({
|
||||
channel: "whatsapp",
|
||||
isTransportEnabled: isWhatsAppApprovalTransportEnabled,
|
||||
hasMatchingTarget: hasMatchingWhatsAppTarget,
|
||||
hasOriginOrSessionTarget: hasWhatsAppOriginOrSessionTarget,
|
||||
});
|
||||
|
||||
const canApprovalPotentiallyRouteToWhatsApp = whatsappApprovalForwarding.isPotentialRoute;
|
||||
const canAnyApprovalPotentiallyRouteToWhatsApp = whatsappApprovalForwarding.canAnyPotentiallyRoute;
|
||||
const isWhatsAppSessionApprovalEligible = whatsappApprovalForwarding.isSessionEligible;
|
||||
const isWhatsAppExplicitTargetEligible = whatsappApprovalForwarding.isExplicitTargetEligible;
|
||||
|
||||
function resolveTurnSourceWhatsAppOriginTarget(
|
||||
request: ApprovalRequest,
|
||||
): WhatsAppApprovalTarget | null {
|
||||
@@ -191,14 +94,23 @@ function resolveSessionWhatsAppOriginTarget(sessionTarget: {
|
||||
return to ? { to, accountId: normalizeOptionalString(sessionTarget.accountId) } : null;
|
||||
}
|
||||
|
||||
function shouldHandleWhatsAppApprovalRequest(params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
approvalKind?: ApprovalKind;
|
||||
request: ApprovalRequest;
|
||||
}): boolean {
|
||||
return whatsappApprovalForwarding.shouldHandleRequest(params);
|
||||
}
|
||||
const whatsappApprovalRouteGates = createNativeApprovalChannelRouteGates({
|
||||
channel: "whatsapp",
|
||||
defaultForwardingMode: DEFAULT_APPROVAL_FORWARDING_MODE,
|
||||
isTransportEnabled: isWhatsAppApprovalTransportEnabled,
|
||||
listAccountIds: listWhatsAppAccountIds,
|
||||
resolveDefaultAccountId: resolveDefaultWhatsAppAccountId,
|
||||
normalizeForwardTarget: normalizeWhatsAppForwardTarget,
|
||||
resolveTurnSourceTarget: resolveTurnSourceWhatsAppOriginTarget,
|
||||
});
|
||||
|
||||
const {
|
||||
canApprovalPotentiallyRouteToChannel: canApprovalPotentiallyRouteToWhatsApp,
|
||||
canAnyApprovalPotentiallyRouteToChannel: canAnyApprovalPotentiallyRouteToWhatsApp,
|
||||
isSessionApprovalEligible: isWhatsAppSessionApprovalEligible,
|
||||
isExplicitTargetEligible: isWhatsAppExplicitTargetEligible,
|
||||
shouldHandleApprovalRequest: shouldHandleWhatsAppApprovalRequest,
|
||||
} = whatsappApprovalRouteGates;
|
||||
|
||||
const resolveWhatsAppOriginTargetBase = createChannelNativeOriginTargetResolver({
|
||||
channel: "whatsapp",
|
||||
|
||||
@@ -42,19 +42,7 @@ const STRICT_LITERAL_STRUCTS = new Set([
|
||||
|
||||
const DEFAULTED_OPTIONAL_INIT_PARAMS: Record<string, Set<string>> = {
|
||||
SessionsAbortParams: new Set(["agentId"]),
|
||||
SessionOperationEvent: new Set(["agentId"]),
|
||||
SessionsUsageParams: new Set(["agentId", "agentScope"]),
|
||||
SessionsCompactionListParams: new Set(["agentId"]),
|
||||
SessionsCompactionGetParams: new Set(["agentId"]),
|
||||
SessionsCompactionBranchParams: new Set(["agentId"]),
|
||||
SessionsCompactionRestoreParams: new Set(["agentId"]),
|
||||
SessionsSendParams: new Set(["agentId"]),
|
||||
SessionsMessagesSubscribeParams: new Set(["agentId"]),
|
||||
SessionsMessagesUnsubscribeParams: new Set(["agentId"]),
|
||||
SessionsPatchParams: new Set(["agentId"]),
|
||||
SessionsResetParams: new Set(["agentId"]),
|
||||
SessionsDeleteParams: new Set(["agentId"]),
|
||||
SessionsCompactParams: new Set(["agentId"]),
|
||||
ArtifactsListParams: new Set(["agentId"]),
|
||||
ArtifactsGetParams: new Set(["agentId"]),
|
||||
ArtifactsDownloadParams: new Set(["agentId"]),
|
||||
|
||||
@@ -927,6 +927,7 @@ export function resetEmbeddedAttemptHarness(
|
||||
}
|
||||
hoisted.createAgentSessionMock.mockReset();
|
||||
hoisted.sessionManagerOpenMock.mockReset().mockReturnValue(hoisted.sessionManager);
|
||||
hoisted.defaultResourceLoaderInitMock.mockReset();
|
||||
hoisted.resolveSandboxContextMock.mockReset();
|
||||
hoisted.ensureGlobalUndiciEnvProxyDispatcherMock.mockReset();
|
||||
hoisted.ensureGlobalUndiciDispatcherStreamTimeoutsMock.mockReset();
|
||||
|
||||
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
createChannelApproverDmTargetResolver,
|
||||
createChannelNativeOriginTargetResolver,
|
||||
createNativeApprovalChannelRouteGates,
|
||||
createNativeApprovalForwardingFallbackSuppressor,
|
||||
type NativeApprovalTarget,
|
||||
nativeApprovalTargetsMatch,
|
||||
@@ -15,6 +16,215 @@ const EMPTY_SESSION_CFG = {
|
||||
},
|
||||
} satisfies OpenClawConfig;
|
||||
|
||||
function createMatrixRouteGates(options?: {
|
||||
enabledAccounts?: readonly string[];
|
||||
accountIds?: readonly string[];
|
||||
defaultAccountId?: string;
|
||||
}) {
|
||||
const enabledAccounts = new Set(options?.enabledAccounts ?? ["default"]);
|
||||
return createNativeApprovalChannelRouteGates<NativeApprovalTarget>({
|
||||
channel: "matrix",
|
||||
defaultForwardingMode: "session",
|
||||
isTransportEnabled: ({ accountId }) => enabledAccounts.has(accountId ?? "default"),
|
||||
listAccountIds: () => options?.accountIds ?? ["default"],
|
||||
resolveDefaultAccountId: () => options?.defaultAccountId ?? "default",
|
||||
normalizeForwardTarget: (target) =>
|
||||
target.channel === "matrix"
|
||||
? {
|
||||
to: target.to,
|
||||
accountId: target.accountId ?? undefined,
|
||||
threadId: target.threadId ?? undefined,
|
||||
}
|
||||
: null,
|
||||
resolveTurnSourceTarget: (request) =>
|
||||
request.request.turnSourceChannel === "matrix" && request.request.turnSourceTo
|
||||
? {
|
||||
to: request.request.turnSourceTo,
|
||||
accountId: request.request.turnSourceAccountId ?? undefined,
|
||||
threadId: request.request.turnSourceThreadId ?? undefined,
|
||||
}
|
||||
: null,
|
||||
});
|
||||
}
|
||||
|
||||
const matrixExecRequest = {
|
||||
id: "req-1",
|
||||
request: {
|
||||
agentId: "agent-a",
|
||||
command: "echo hi",
|
||||
sessionKey: "agent:agent-a:matrix:room-1",
|
||||
turnSourceAccountId: "default",
|
||||
turnSourceChannel: "matrix",
|
||||
turnSourceTo: "room-1",
|
||||
},
|
||||
createdAtMs: 0,
|
||||
expiresAtMs: 1000,
|
||||
} as const;
|
||||
|
||||
const matrixPluginRequest = {
|
||||
id: "plugin:req-1",
|
||||
request: {
|
||||
agentId: "agent-a",
|
||||
description: "Allow access",
|
||||
sessionKey: "agent:agent-a:matrix:room-1",
|
||||
title: "Plugin approval",
|
||||
turnSourceAccountId: "default",
|
||||
turnSourceChannel: "matrix",
|
||||
turnSourceTo: "room-1",
|
||||
},
|
||||
createdAtMs: 0,
|
||||
expiresAtMs: 1000,
|
||||
} as const;
|
||||
|
||||
describe("createNativeApprovalChannelRouteGates", () => {
|
||||
it("separates session-native and explicit target routing by approval family", () => {
|
||||
const gates = createMatrixRouteGates();
|
||||
const cfg = {
|
||||
approvals: {
|
||||
exec: {
|
||||
enabled: true,
|
||||
mode: "targets",
|
||||
targets: [{ channel: "matrix", to: "room-1" }],
|
||||
},
|
||||
plugin: {
|
||||
enabled: true,
|
||||
mode: "session",
|
||||
},
|
||||
},
|
||||
} satisfies OpenClawConfig;
|
||||
|
||||
expect(
|
||||
gates.canApprovalPotentiallyRouteToChannel({
|
||||
cfg,
|
||||
approvalKind: "exec",
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
gates.canApprovalPotentiallyRouteToChannel({
|
||||
cfg,
|
||||
approvalKind: "exec",
|
||||
nativeSessionOnly: true,
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
gates.canApprovalPotentiallyRouteToChannel({
|
||||
cfg,
|
||||
approvalKind: "plugin",
|
||||
nativeSessionOnly: true,
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(gates.isNativeApprovalHandlerConfigured({ cfg })).toBe(true);
|
||||
|
||||
expect(
|
||||
gates.shouldHandleApprovalRequest({
|
||||
cfg,
|
||||
request: matrixExecRequest,
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
gates.shouldHandleApprovalRequest({
|
||||
cfg,
|
||||
request: matrixPluginRequest,
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
gates.isExplicitTargetEligible({
|
||||
cfg,
|
||||
approvalKind: "exec",
|
||||
request: matrixExecRequest,
|
||||
target: { channel: "matrix", to: "room-1", source: "target" },
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("applies forwarding filters before accepting a session route", () => {
|
||||
const gates = createMatrixRouteGates();
|
||||
const cfg = {
|
||||
approvals: {
|
||||
exec: {
|
||||
enabled: true,
|
||||
agentFilter: ["agent-a"],
|
||||
sessionFilter: ["matrix:room"],
|
||||
},
|
||||
},
|
||||
} satisfies OpenClawConfig;
|
||||
|
||||
expect(
|
||||
gates.isSessionApprovalEligible({
|
||||
cfg,
|
||||
approvalKind: "exec",
|
||||
request: matrixExecRequest,
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(
|
||||
gates.isSessionApprovalEligible({
|
||||
cfg,
|
||||
approvalKind: "exec",
|
||||
request: {
|
||||
...matrixExecRequest,
|
||||
request: {
|
||||
...matrixExecRequest.request,
|
||||
agentId: "agent-b",
|
||||
sessionKey: "agent:agent-b:matrix:room-1",
|
||||
},
|
||||
},
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("uses default and single-enabled account fallback for unscoped targets", () => {
|
||||
const cfg = {
|
||||
approvals: {
|
||||
exec: {
|
||||
enabled: true,
|
||||
mode: "targets",
|
||||
targets: [{ channel: "matrix", to: "room-1" }],
|
||||
},
|
||||
},
|
||||
} satisfies OpenClawConfig;
|
||||
const target = { channel: "matrix", to: "room-1", source: "target" } as const;
|
||||
|
||||
expect(
|
||||
createMatrixRouteGates({
|
||||
accountIds: ["default", "work"],
|
||||
enabledAccounts: ["default", "work"],
|
||||
}).isExplicitTargetEligible({
|
||||
cfg,
|
||||
accountId: "default",
|
||||
approvalKind: "exec",
|
||||
request: matrixExecRequest,
|
||||
target,
|
||||
}),
|
||||
).toBe(true);
|
||||
|
||||
expect(
|
||||
createMatrixRouteGates({
|
||||
accountIds: ["default", "work"],
|
||||
enabledAccounts: ["work"],
|
||||
}).isExplicitTargetEligible({
|
||||
cfg,
|
||||
accountId: "work",
|
||||
approvalKind: "exec",
|
||||
request: matrixExecRequest,
|
||||
target,
|
||||
}),
|
||||
).toBe(true);
|
||||
|
||||
expect(
|
||||
createMatrixRouteGates({
|
||||
accountIds: ["default", "work"],
|
||||
enabledAccounts: ["default", "work"],
|
||||
}).isExplicitTargetEligible({
|
||||
cfg,
|
||||
accountId: "work",
|
||||
approvalKind: "exec",
|
||||
request: matrixExecRequest,
|
||||
target,
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("createChannelNativeOriginTargetResolver", () => {
|
||||
it("reuses shared turn-source routing and respects shouldHandle gating", () => {
|
||||
const resolveOriginTarget = createChannelNativeOriginTargetResolver<NativeApprovalTarget>({
|
||||
|
||||
@@ -9,9 +9,17 @@ import {
|
||||
type ExecApprovalReplyMetadata,
|
||||
} from "../infra/exec-approval-reply.js";
|
||||
import type { ExecApprovalSessionTarget } from "../infra/exec-approval-session-target.js";
|
||||
import { resolveApprovalRequestOriginTarget } from "../infra/exec-approval-session-target.js";
|
||||
import {
|
||||
resolveApprovalRequestOriginTarget,
|
||||
resolveApprovalRequestSessionTarget,
|
||||
} from "../infra/exec-approval-session-target.js";
|
||||
import type { ExecApprovalRequest } from "../infra/exec-approvals.js";
|
||||
import type { PluginApprovalRequest } from "../infra/plugin-approvals.js";
|
||||
import { normalizeAccountId } from "../routing/session-key.js";
|
||||
import {
|
||||
normalizeLowercaseStringOrEmpty,
|
||||
normalizeOptionalString,
|
||||
} from "../shared/string-coerce.js";
|
||||
import type { ChannelApprovalCapability, ChannelOutboundPayloadHint } from "./channel-contract.js";
|
||||
import { channelRouteTargetsMatchExact } from "./channel-route.js";
|
||||
import type { OpenClawConfig } from "./config-runtime.js";
|
||||
@@ -127,6 +135,54 @@ type NativeApprovalForwardingFallbackSuppressorParams<TTarget extends NativeAppr
|
||||
targetsMatch?: (left: TTarget, right: TTarget) => boolean;
|
||||
};
|
||||
|
||||
type NativeApprovalChannelRouteGateParams<TTarget extends NativeApprovalTarget> = {
|
||||
channel: string;
|
||||
defaultForwardingMode: ExecApprovalForwardingMode;
|
||||
isTransportEnabled: (params: { cfg: OpenClawConfig; accountId?: string | null }) => boolean;
|
||||
listAccountIds: (cfg: OpenClawConfig) => readonly string[];
|
||||
resolveDefaultAccountId: (cfg: OpenClawConfig) => string;
|
||||
normalizeForwardTarget: (target: NativeApprovalForwardTarget) => TTarget | null;
|
||||
resolveTurnSourceTarget: (request: ApprovalRequest) => TTarget | null;
|
||||
targetsMatch?: (left: TTarget, right: TTarget) => boolean;
|
||||
};
|
||||
|
||||
type NativeApprovalChannelRouteGates = {
|
||||
canApprovalPotentiallyRouteToChannel: (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
approvalKind: ApprovalKind;
|
||||
nativeSessionOnly?: boolean;
|
||||
}) => boolean;
|
||||
canAnyApprovalPotentiallyRouteToChannel: (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
nativeSessionOnly?: boolean;
|
||||
}) => boolean;
|
||||
isNativeApprovalHandlerConfigured: (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
}) => boolean;
|
||||
isSessionApprovalEligible: (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
approvalKind: ApprovalKind;
|
||||
request: ApprovalRequest;
|
||||
}) => boolean;
|
||||
isExplicitTargetEligible: (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
approvalKind: ApprovalKind;
|
||||
request: ApprovalRequest;
|
||||
target: NativeApprovalForwardTarget;
|
||||
}) => boolean;
|
||||
shouldHandleApprovalRequest: (params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
approvalKind?: ApprovalKind;
|
||||
request: ApprovalRequest;
|
||||
}) => boolean;
|
||||
};
|
||||
|
||||
type NativeOriginResolverParams<TTarget extends NativeApprovalTarget> = {
|
||||
channel: string;
|
||||
shouldHandleRequest?: (params: ApprovalResolverParams) => boolean;
|
||||
@@ -435,6 +491,242 @@ export function createChannelApprovalForwardingEvaluator(
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeApprovalForwardingModeWithDefault(params: {
|
||||
config: ExecApprovalForwardingConfig;
|
||||
defaultForwardingMode: ExecApprovalForwardingMode;
|
||||
}): ExecApprovalForwardingMode {
|
||||
return params.config.mode ?? params.defaultForwardingMode;
|
||||
}
|
||||
|
||||
export function createNativeApprovalChannelRouteGates<TTarget extends NativeApprovalTarget>(
|
||||
params: NativeApprovalChannelRouteGateParams<TTarget>,
|
||||
): NativeApprovalChannelRouteGates {
|
||||
const targetsMatch =
|
||||
params.targetsMatch ??
|
||||
((left: TTarget, right: TTarget) =>
|
||||
nativeApprovalTargetsMatch({ channel: params.channel, left, right }));
|
||||
|
||||
const targetAccountMatchesChannelAccount = (input: {
|
||||
cfg: OpenClawConfig;
|
||||
targetAccountId?: string | null;
|
||||
accountId?: string | null;
|
||||
}): boolean => {
|
||||
const targetAccountId = normalizeOptionalString(input.targetAccountId);
|
||||
const accountId = normalizeOptionalString(input.accountId);
|
||||
if (targetAccountId) {
|
||||
return !accountId || normalizeAccountId(targetAccountId) === normalizeAccountId(accountId);
|
||||
}
|
||||
if (!accountId) {
|
||||
return true;
|
||||
}
|
||||
const normalizedAccountId = normalizeAccountId(accountId);
|
||||
const defaultAccountId = normalizeAccountId(params.resolveDefaultAccountId(input.cfg));
|
||||
if (normalizedAccountId === defaultAccountId) {
|
||||
return true;
|
||||
}
|
||||
const enabledAccountIds = params
|
||||
.listAccountIds(input.cfg)
|
||||
.filter((candidateAccountId) =>
|
||||
params.isTransportEnabled({
|
||||
cfg: input.cfg,
|
||||
accountId: candidateAccountId,
|
||||
}),
|
||||
)
|
||||
.map((candidateAccountId) => normalizeAccountId(candidateAccountId));
|
||||
return enabledAccountIds.length === 1 && enabledAccountIds[0] === normalizedAccountId;
|
||||
};
|
||||
|
||||
const hasMatchingChannelTarget = (input: {
|
||||
cfg: OpenClawConfig;
|
||||
config: ExecApprovalForwardingConfig;
|
||||
accountId?: string | null;
|
||||
target?: NativeApprovalForwardTarget;
|
||||
}): boolean => {
|
||||
const candidateTarget = input.target ? params.normalizeForwardTarget(input.target) : null;
|
||||
return (input.config.targets ?? []).some((target) => {
|
||||
const configuredTarget = params.normalizeForwardTarget(target);
|
||||
if (!configuredTarget) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
!targetAccountMatchesChannelAccount({
|
||||
cfg: input.cfg,
|
||||
targetAccountId: configuredTarget.accountId,
|
||||
accountId: input.accountId,
|
||||
})
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (!candidateTarget) {
|
||||
return true;
|
||||
}
|
||||
return targetsMatch(configuredTarget, candidateTarget);
|
||||
});
|
||||
};
|
||||
|
||||
const hasChannelOriginOrSessionTarget = (input: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
request: ApprovalRequest;
|
||||
}): boolean => {
|
||||
if (params.resolveTurnSourceTarget(input.request)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const sessionTarget = resolveApprovalRequestSessionTarget({
|
||||
cfg: input.cfg,
|
||||
request: input.request,
|
||||
});
|
||||
return (
|
||||
normalizeLowercaseStringOrEmpty(sessionTarget?.channel) === params.channel &&
|
||||
targetAccountMatchesChannelAccount({
|
||||
cfg: input.cfg,
|
||||
targetAccountId: sessionTarget?.accountId,
|
||||
accountId: input.accountId,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const canApprovalPotentiallyRouteToChannel = (input: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
approvalKind: ApprovalKind;
|
||||
nativeSessionOnly?: boolean;
|
||||
}): boolean => {
|
||||
if (!params.isTransportEnabled(input)) {
|
||||
return false;
|
||||
}
|
||||
const config = resolveApprovalForwardingConfig(input);
|
||||
if (!config?.enabled) {
|
||||
return false;
|
||||
}
|
||||
const mode = normalizeApprovalForwardingModeWithDefault({
|
||||
config,
|
||||
defaultForwardingMode: params.defaultForwardingMode,
|
||||
});
|
||||
if (approvalModeIncludesSession(mode)) {
|
||||
return true;
|
||||
}
|
||||
if (input.nativeSessionOnly) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
approvalModeIncludesTargets(mode) &&
|
||||
hasMatchingChannelTarget({
|
||||
cfg: input.cfg,
|
||||
config,
|
||||
accountId: input.accountId,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const canAnyApprovalPotentiallyRouteToChannel = (input: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
nativeSessionOnly?: boolean;
|
||||
}): boolean =>
|
||||
canApprovalPotentiallyRouteToChannel({
|
||||
...input,
|
||||
approvalKind: "exec",
|
||||
}) ||
|
||||
canApprovalPotentiallyRouteToChannel({
|
||||
...input,
|
||||
approvalKind: "plugin",
|
||||
});
|
||||
|
||||
const isSessionApprovalEligible = (input: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
approvalKind: ApprovalKind;
|
||||
request: ApprovalRequest;
|
||||
}): boolean => {
|
||||
if (!params.isTransportEnabled(input)) {
|
||||
return false;
|
||||
}
|
||||
const config = resolveApprovalForwardingConfig(input);
|
||||
if (!config?.enabled) {
|
||||
return false;
|
||||
}
|
||||
const mode = normalizeApprovalForwardingModeWithDefault({
|
||||
config,
|
||||
defaultForwardingMode: params.defaultForwardingMode,
|
||||
});
|
||||
if (!approvalModeIncludesSession(mode)) {
|
||||
return false;
|
||||
}
|
||||
if (!matchesForwardingFilters({ config, request: input.request })) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
!doesApprovalRequestMatchChannelAccount({
|
||||
cfg: input.cfg,
|
||||
request: input.request,
|
||||
channel: params.channel,
|
||||
accountId: input.accountId,
|
||||
})
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return hasChannelOriginOrSessionTarget(input);
|
||||
};
|
||||
|
||||
const isExplicitTargetEligible = (input: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
approvalKind: ApprovalKind;
|
||||
request: ApprovalRequest;
|
||||
target: NativeApprovalForwardTarget;
|
||||
}): boolean => {
|
||||
if (!params.isTransportEnabled(input)) {
|
||||
return false;
|
||||
}
|
||||
const config = resolveApprovalForwardingConfig(input);
|
||||
if (!config?.enabled) {
|
||||
return false;
|
||||
}
|
||||
const mode = normalizeApprovalForwardingModeWithDefault({
|
||||
config,
|
||||
defaultForwardingMode: params.defaultForwardingMode,
|
||||
});
|
||||
if (!approvalModeIncludesTargets(mode)) {
|
||||
return false;
|
||||
}
|
||||
if (!matchesForwardingFilters({ config, request: input.request })) {
|
||||
return false;
|
||||
}
|
||||
return hasMatchingChannelTarget({
|
||||
cfg: input.cfg,
|
||||
config,
|
||||
accountId: input.accountId,
|
||||
target: input.target,
|
||||
});
|
||||
};
|
||||
|
||||
const shouldHandleApprovalRequest = (input: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
approvalKind?: ApprovalKind;
|
||||
request: ApprovalRequest;
|
||||
}): boolean =>
|
||||
isSessionApprovalEligible({
|
||||
...input,
|
||||
approvalKind: resolveApprovalKind(input.request, input.approvalKind),
|
||||
});
|
||||
|
||||
return {
|
||||
canApprovalPotentiallyRouteToChannel,
|
||||
canAnyApprovalPotentiallyRouteToChannel,
|
||||
isNativeApprovalHandlerConfigured: (input) =>
|
||||
canAnyApprovalPotentiallyRouteToChannel({
|
||||
...input,
|
||||
nativeSessionOnly: true,
|
||||
}),
|
||||
isSessionApprovalEligible,
|
||||
isExplicitTargetEligible,
|
||||
shouldHandleApprovalRequest,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeOptionalAccountId(value?: string | null): string | undefined {
|
||||
return value?.trim() || undefined;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ export {
|
||||
createChannelApprovalForwardingEvaluator,
|
||||
createChannelApproverDmTargetResolver,
|
||||
createChannelNativeOriginTargetResolver,
|
||||
createNativeApprovalChannelRouteGates,
|
||||
createNativeApprovalForwardingFallbackSuppressor,
|
||||
nativeApprovalTargetsMatch,
|
||||
resolveApprovalKind,
|
||||
|
||||
8
ui/src/i18n/.i18n/ar.meta.json
generated
8
ui/src/i18n/.i18n/ar.meta.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-29T18:10:07.578Z",
|
||||
"generatedAt": "2026-05-29T21:01:05.561Z",
|
||||
"locale": "ar",
|
||||
"model": "gpt-5.5",
|
||||
"provider": "openai",
|
||||
"sourceHash": "1ddd9306566b37255394133ec48e05dfe9dd502fd57a34d3c9c96152331730e1",
|
||||
"totalKeys": 1269,
|
||||
"translatedKeys": 1269,
|
||||
"sourceHash": "d51c5347e718c0fbe7353fccf82cefc0ef09b95b25acbc840a2f19b0c17b9296",
|
||||
"totalKeys": 1271,
|
||||
"translatedKeys": 1271,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
2
ui/src/i18n/.i18n/ar.tm.jsonl
generated
Normal file
2
ui/src/i18n/.i18n/ar.tm.jsonl
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
{"cache_key":"31baf5b9e4f804c6e3a61e02532c991cbb3d27c24b36ad51b9505cdc8bdf9f97","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goalNote","source_path":"ui/src/i18n/locales/ar.ts","src_lang":"en","text":"Goal note","text_hash":"1afb7855a394ef7078728de1c804d6b995413db4eafe7d74190076cb9ed2c9f5","tgt_lang":"ar","translated":"ملاحظة الهدف","updated_at":"2026-05-29T21:01:05.556Z"}
|
||||
{"cache_key":"b83a08557f604af51d1d904a15fd37755f3e3f311e32b5dff3c7218cfd75183d","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goal","source_path":"ui/src/i18n/locales/ar.ts","src_lang":"en","text":"Goal","text_hash":"cdbf6975e8a35b0d03558be6822dfae166482c24fb86b0433f60e8167f5c91e4","tgt_lang":"ar","translated":"الهدف","updated_at":"2026-05-29T21:01:05.553Z"}
|
||||
8
ui/src/i18n/.i18n/de.meta.json
generated
8
ui/src/i18n/.i18n/de.meta.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-29T18:09:57.996Z",
|
||||
"generatedAt": "2026-05-29T21:00:05.360Z",
|
||||
"locale": "de",
|
||||
"model": "gpt-5.5",
|
||||
"provider": "openai",
|
||||
"sourceHash": "1ddd9306566b37255394133ec48e05dfe9dd502fd57a34d3c9c96152331730e1",
|
||||
"totalKeys": 1269,
|
||||
"translatedKeys": 1269,
|
||||
"sourceHash": "d51c5347e718c0fbe7353fccf82cefc0ef09b95b25acbc840a2f19b0c17b9296",
|
||||
"totalKeys": 1271,
|
||||
"translatedKeys": 1271,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
2
ui/src/i18n/.i18n/de.tm.jsonl
generated
Normal file
2
ui/src/i18n/.i18n/de.tm.jsonl
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
{"cache_key":"3291b27d87c9b085d83645bf3d093be323f1809505f54b9edf8bd2691601fde6","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goal","source_path":"ui/src/i18n/locales/de.ts","src_lang":"en","text":"Goal","text_hash":"cdbf6975e8a35b0d03558be6822dfae166482c24fb86b0433f60e8167f5c91e4","tgt_lang":"de","translated":"Ziel","updated_at":"2026-05-29T21:00:05.331Z"}
|
||||
{"cache_key":"e50aa42b44fa61de5322d01701b08a1460b315ebf5beb258a7b68d1c40200a02","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goalNote","source_path":"ui/src/i18n/locales/de.ts","src_lang":"en","text":"Goal note","text_hash":"1afb7855a394ef7078728de1c804d6b995413db4eafe7d74190076cb9ed2c9f5","tgt_lang":"de","translated":"Zielnotiz","updated_at":"2026-05-29T21:00:05.331Z"}
|
||||
8
ui/src/i18n/.i18n/es.meta.json
generated
8
ui/src/i18n/.i18n/es.meta.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-29T18:10:00.520Z",
|
||||
"generatedAt": "2026-05-29T21:00:16.244Z",
|
||||
"locale": "es",
|
||||
"model": "gpt-5.5",
|
||||
"provider": "openai",
|
||||
"sourceHash": "1ddd9306566b37255394133ec48e05dfe9dd502fd57a34d3c9c96152331730e1",
|
||||
"totalKeys": 1269,
|
||||
"translatedKeys": 1269,
|
||||
"sourceHash": "d51c5347e718c0fbe7353fccf82cefc0ef09b95b25acbc840a2f19b0c17b9296",
|
||||
"totalKeys": 1271,
|
||||
"translatedKeys": 1271,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
2
ui/src/i18n/.i18n/es.tm.jsonl
generated
Normal file
2
ui/src/i18n/.i18n/es.tm.jsonl
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
{"cache_key":"9d69982042ae98028cd211bab9a9b5f33b1ddaa2a7ee8231a6540e9e63302e25","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goalNote","source_path":"ui/src/i18n/locales/es.ts","src_lang":"en","text":"Goal note","text_hash":"1afb7855a394ef7078728de1c804d6b995413db4eafe7d74190076cb9ed2c9f5","tgt_lang":"es","translated":"Nota del objetivo","updated_at":"2026-05-29T21:00:16.236Z"}
|
||||
{"cache_key":"b626995f71c58bfb263806f12b5eece5091a529999f3f1bfccbc5df135f6a178","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goal","source_path":"ui/src/i18n/locales/es.ts","src_lang":"en","text":"Goal","text_hash":"cdbf6975e8a35b0d03558be6822dfae166482c24fb86b0433f60e8167f5c91e4","tgt_lang":"es","translated":"Objetivo","updated_at":"2026-05-29T21:00:16.236Z"}
|
||||
8
ui/src/i18n/.i18n/fa.meta.json
generated
8
ui/src/i18n/.i18n/fa.meta.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-29T18:10:21.090Z",
|
||||
"generatedAt": "2026-05-29T21:02:42.653Z",
|
||||
"locale": "fa",
|
||||
"model": "gpt-5.5",
|
||||
"provider": "openai",
|
||||
"sourceHash": "1ddd9306566b37255394133ec48e05dfe9dd502fd57a34d3c9c96152331730e1",
|
||||
"totalKeys": 1269,
|
||||
"translatedKeys": 1269,
|
||||
"sourceHash": "d51c5347e718c0fbe7353fccf82cefc0ef09b95b25acbc840a2f19b0c17b9296",
|
||||
"totalKeys": 1271,
|
||||
"translatedKeys": 1271,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
2
ui/src/i18n/.i18n/fa.tm.jsonl
generated
Normal file
2
ui/src/i18n/.i18n/fa.tm.jsonl
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
{"cache_key":"4a2cd58d849e04841d7cc035ec2c90d722c4fed30f648da173044275addefb73","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goalNote","source_path":"ui/src/i18n/locales/fa.ts","src_lang":"en","text":"Goal note","text_hash":"1afb7855a394ef7078728de1c804d6b995413db4eafe7d74190076cb9ed2c9f5","tgt_lang":"fa","translated":"یادداشت هدف","updated_at":"2026-05-29T21:02:42.628Z"}
|
||||
{"cache_key":"71d09491009ef87d3af376ce4162e9b18d97162c0d76ba6553a489cc3cd638c6","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goal","source_path":"ui/src/i18n/locales/fa.ts","src_lang":"en","text":"Goal","text_hash":"cdbf6975e8a35b0d03558be6822dfae166482c24fb86b0433f60e8167f5c91e4","tgt_lang":"fa","translated":"هدف","updated_at":"2026-05-29T21:02:42.628Z"}
|
||||
8
ui/src/i18n/.i18n/fr.meta.json
generated
8
ui/src/i18n/.i18n/fr.meta.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-29T18:10:06.134Z",
|
||||
"generatedAt": "2026-05-29T21:00:46.756Z",
|
||||
"locale": "fr",
|
||||
"model": "gpt-5.5",
|
||||
"provider": "openai",
|
||||
"sourceHash": "1ddd9306566b37255394133ec48e05dfe9dd502fd57a34d3c9c96152331730e1",
|
||||
"totalKeys": 1269,
|
||||
"translatedKeys": 1269,
|
||||
"sourceHash": "d51c5347e718c0fbe7353fccf82cefc0ef09b95b25acbc840a2f19b0c17b9296",
|
||||
"totalKeys": 1271,
|
||||
"translatedKeys": 1271,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
2
ui/src/i18n/.i18n/fr.tm.jsonl
generated
Normal file
2
ui/src/i18n/.i18n/fr.tm.jsonl
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
{"cache_key":"a87418ac5fda24f3add83f591aa5c4b8ebd1d3914f11b428e8eac9cc3b959fc5","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goalNote","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Goal note","text_hash":"1afb7855a394ef7078728de1c804d6b995413db4eafe7d74190076cb9ed2c9f5","tgt_lang":"fr","translated":"Note d’objectif","updated_at":"2026-05-29T21:00:46.685Z"}
|
||||
{"cache_key":"d0488f998b840872ae71bc8d8209e714ef16aa4216a6b422006b4762744e1289","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goal","source_path":"ui/src/i18n/locales/fr.ts","src_lang":"en","text":"Goal","text_hash":"cdbf6975e8a35b0d03558be6822dfae166482c24fb86b0433f60e8167f5c91e4","tgt_lang":"fr","translated":"Objectif","updated_at":"2026-05-29T21:00:46.685Z"}
|
||||
8
ui/src/i18n/.i18n/id.meta.json
generated
8
ui/src/i18n/.i18n/id.meta.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-29T18:10:13.111Z",
|
||||
"generatedAt": "2026-05-29T21:01:44.176Z",
|
||||
"locale": "id",
|
||||
"model": "gpt-5.5",
|
||||
"provider": "openai",
|
||||
"sourceHash": "1ddd9306566b37255394133ec48e05dfe9dd502fd57a34d3c9c96152331730e1",
|
||||
"totalKeys": 1269,
|
||||
"translatedKeys": 1269,
|
||||
"sourceHash": "d51c5347e718c0fbe7353fccf82cefc0ef09b95b25acbc840a2f19b0c17b9296",
|
||||
"totalKeys": 1271,
|
||||
"translatedKeys": 1271,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
2
ui/src/i18n/.i18n/id.tm.jsonl
generated
Normal file
2
ui/src/i18n/.i18n/id.tm.jsonl
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
{"cache_key":"2b375a43961fdfcfdd974503b29314f06a33027760f2592eace136fda36732fd","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goalNote","source_path":"ui/src/i18n/locales/id.ts","src_lang":"en","text":"Goal note","text_hash":"1afb7855a394ef7078728de1c804d6b995413db4eafe7d74190076cb9ed2c9f5","tgt_lang":"id","translated":"Catatan tujuan","updated_at":"2026-05-29T21:01:43.534Z"}
|
||||
{"cache_key":"7781d543931e105b5b122201b4f1913e29c028cc7b6e0695c5a91ec2d84dce0c","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goal","source_path":"ui/src/i18n/locales/id.ts","src_lang":"en","text":"Goal","text_hash":"cdbf6975e8a35b0d03558be6822dfae166482c24fb86b0433f60e8167f5c91e4","tgt_lang":"id","translated":"Tujuan","updated_at":"2026-05-29T21:01:43.511Z"}
|
||||
8
ui/src/i18n/.i18n/it.meta.json
generated
8
ui/src/i18n/.i18n/it.meta.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-29T18:10:08.751Z",
|
||||
"generatedAt": "2026-05-29T21:01:19.882Z",
|
||||
"locale": "it",
|
||||
"model": "gpt-5.5",
|
||||
"provider": "openai",
|
||||
"sourceHash": "1ddd9306566b37255394133ec48e05dfe9dd502fd57a34d3c9c96152331730e1",
|
||||
"totalKeys": 1269,
|
||||
"translatedKeys": 1269,
|
||||
"sourceHash": "d51c5347e718c0fbe7353fccf82cefc0ef09b95b25acbc840a2f19b0c17b9296",
|
||||
"totalKeys": 1271,
|
||||
"translatedKeys": 1271,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
2
ui/src/i18n/.i18n/it.tm.jsonl
generated
Normal file
2
ui/src/i18n/.i18n/it.tm.jsonl
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
{"cache_key":"10f9390d7ed31c5188b9663412768c8d085c4311c703765a09cee4309636e530","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goalNote","source_path":"ui/src/i18n/locales/it.ts","src_lang":"en","text":"Goal note","text_hash":"1afb7855a394ef7078728de1c804d6b995413db4eafe7d74190076cb9ed2c9f5","tgt_lang":"it","translated":"Nota sull'obiettivo","updated_at":"2026-05-29T21:01:19.873Z"}
|
||||
{"cache_key":"31e985892e47fba1b0d3c69810e7c70676c475936ca1382069c2ccc3c82a6a85","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goal","source_path":"ui/src/i18n/locales/it.ts","src_lang":"en","text":"Goal","text_hash":"cdbf6975e8a35b0d03558be6822dfae166482c24fb86b0433f60e8167f5c91e4","tgt_lang":"it","translated":"Obiettivo","updated_at":"2026-05-29T21:01:19.873Z"}
|
||||
8
ui/src/i18n/.i18n/ja-JP.meta.json
generated
8
ui/src/i18n/.i18n/ja-JP.meta.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-29T18:10:01.779Z",
|
||||
"generatedAt": "2026-05-29T21:00:25.952Z",
|
||||
"locale": "ja-JP",
|
||||
"model": "gpt-5.5",
|
||||
"provider": "openai",
|
||||
"sourceHash": "1ddd9306566b37255394133ec48e05dfe9dd502fd57a34d3c9c96152331730e1",
|
||||
"totalKeys": 1269,
|
||||
"translatedKeys": 1269,
|
||||
"sourceHash": "d51c5347e718c0fbe7353fccf82cefc0ef09b95b25acbc840a2f19b0c17b9296",
|
||||
"totalKeys": 1271,
|
||||
"translatedKeys": 1271,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
2
ui/src/i18n/.i18n/ja-JP.tm.jsonl
generated
Normal file
2
ui/src/i18n/.i18n/ja-JP.tm.jsonl
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
{"cache_key":"385cee2f0cb4a83b0e53424167bef43a488ba00da62ea56753a393ec6b6960b2","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goal","source_path":"ui/src/i18n/locales/ja-JP.ts","src_lang":"en","text":"Goal","text_hash":"cdbf6975e8a35b0d03558be6822dfae166482c24fb86b0433f60e8167f5c91e4","tgt_lang":"ja-JP","translated":"目標","updated_at":"2026-05-29T21:00:25.945Z"}
|
||||
{"cache_key":"d18570791fe4a33cac91265fb875e2e15cb4c0172d17e4ba14bd8208a1df3401","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goalNote","source_path":"ui/src/i18n/locales/ja-JP.ts","src_lang":"en","text":"Goal note","text_hash":"1afb7855a394ef7078728de1c804d6b995413db4eafe7d74190076cb9ed2c9f5","tgt_lang":"ja-JP","translated":"目標メモ","updated_at":"2026-05-29T21:00:25.945Z"}
|
||||
8
ui/src/i18n/.i18n/ko.meta.json
generated
8
ui/src/i18n/.i18n/ko.meta.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-29T18:10:03.892Z",
|
||||
"generatedAt": "2026-05-29T21:00:36.567Z",
|
||||
"locale": "ko",
|
||||
"model": "gpt-5.5",
|
||||
"provider": "openai",
|
||||
"sourceHash": "1ddd9306566b37255394133ec48e05dfe9dd502fd57a34d3c9c96152331730e1",
|
||||
"totalKeys": 1269,
|
||||
"translatedKeys": 1269,
|
||||
"sourceHash": "d51c5347e718c0fbe7353fccf82cefc0ef09b95b25acbc840a2f19b0c17b9296",
|
||||
"totalKeys": 1271,
|
||||
"translatedKeys": 1271,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
2
ui/src/i18n/.i18n/ko.tm.jsonl
generated
Normal file
2
ui/src/i18n/.i18n/ko.tm.jsonl
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
{"cache_key":"6661a8580f033e530995fca7fa9a21141d4c43f499c3bac4b5bcde8e4f085a50","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goalNote","source_path":"ui/src/i18n/locales/ko.ts","src_lang":"en","text":"Goal note","text_hash":"1afb7855a394ef7078728de1c804d6b995413db4eafe7d74190076cb9ed2c9f5","tgt_lang":"ko","translated":"목표 메모","updated_at":"2026-05-29T21:00:36.520Z"}
|
||||
{"cache_key":"c4aa03b43d324f829c9cea82c3296e21c9b65f7388dba7979b5dd57318a5f957","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goal","source_path":"ui/src/i18n/locales/ko.ts","src_lang":"en","text":"Goal","text_hash":"cdbf6975e8a35b0d03558be6822dfae166482c24fb86b0433f60e8167f5c91e4","tgt_lang":"ko","translated":"목표","updated_at":"2026-05-29T21:00:36.520Z"}
|
||||
8
ui/src/i18n/.i18n/nl.meta.json
generated
8
ui/src/i18n/.i18n/nl.meta.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-29T18:10:19.671Z",
|
||||
"generatedAt": "2026-05-29T21:02:33.330Z",
|
||||
"locale": "nl",
|
||||
"model": "gpt-5.5",
|
||||
"provider": "openai",
|
||||
"sourceHash": "1ddd9306566b37255394133ec48e05dfe9dd502fd57a34d3c9c96152331730e1",
|
||||
"totalKeys": 1269,
|
||||
"translatedKeys": 1269,
|
||||
"sourceHash": "d51c5347e718c0fbe7353fccf82cefc0ef09b95b25acbc840a2f19b0c17b9296",
|
||||
"totalKeys": 1271,
|
||||
"translatedKeys": 1271,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
2
ui/src/i18n/.i18n/nl.tm.jsonl
generated
Normal file
2
ui/src/i18n/.i18n/nl.tm.jsonl
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
{"cache_key":"61322d7dd6c6f6ecd12429935bddd35cf29c24771475df989fb28ab6a579b26b","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goalNote","source_path":"ui/src/i18n/locales/nl.ts","src_lang":"en","text":"Goal note","text_hash":"1afb7855a394ef7078728de1c804d6b995413db4eafe7d74190076cb9ed2c9f5","tgt_lang":"nl","translated":"Doelnotitie","updated_at":"2026-05-29T21:02:33.311Z"}
|
||||
{"cache_key":"e6f48f62b167833335d714cc2380e3b71081a01e7888678832b137bde4f4a747","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goal","source_path":"ui/src/i18n/locales/nl.ts","src_lang":"en","text":"Goal","text_hash":"cdbf6975e8a35b0d03558be6822dfae166482c24fb86b0433f60e8167f5c91e4","tgt_lang":"nl","translated":"Doel","updated_at":"2026-05-29T21:02:33.311Z"}
|
||||
8
ui/src/i18n/.i18n/pl.meta.json
generated
8
ui/src/i18n/.i18n/pl.meta.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-29T18:10:14.758Z",
|
||||
"generatedAt": "2026-05-29T21:02:00.415Z",
|
||||
"locale": "pl",
|
||||
"model": "gpt-5.5",
|
||||
"provider": "openai",
|
||||
"sourceHash": "1ddd9306566b37255394133ec48e05dfe9dd502fd57a34d3c9c96152331730e1",
|
||||
"totalKeys": 1269,
|
||||
"translatedKeys": 1269,
|
||||
"sourceHash": "d51c5347e718c0fbe7353fccf82cefc0ef09b95b25acbc840a2f19b0c17b9296",
|
||||
"totalKeys": 1271,
|
||||
"translatedKeys": 1271,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
2
ui/src/i18n/.i18n/pl.tm.jsonl
generated
Normal file
2
ui/src/i18n/.i18n/pl.tm.jsonl
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
{"cache_key":"6c2c3123ea0674e55540119ca248b61ae9e719eb05a4140cda4be98d621b39e2","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goalNote","source_path":"ui/src/i18n/locales/pl.ts","src_lang":"en","text":"Goal note","text_hash":"1afb7855a394ef7078728de1c804d6b995413db4eafe7d74190076cb9ed2c9f5","tgt_lang":"pl","translated":"Notatka dotycząca celu","updated_at":"2026-05-29T21:02:00.343Z"}
|
||||
{"cache_key":"773fde7f04e9c87f69cc58984950611863dcac306cfbb15353f410e900533db6","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goal","source_path":"ui/src/i18n/locales/pl.ts","src_lang":"en","text":"Goal","text_hash":"cdbf6975e8a35b0d03558be6822dfae166482c24fb86b0433f60e8167f5c91e4","tgt_lang":"pl","translated":"Cel","updated_at":"2026-05-29T21:02:00.339Z"}
|
||||
8
ui/src/i18n/.i18n/pt-BR.meta.json
generated
8
ui/src/i18n/.i18n/pt-BR.meta.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-29T18:09:56.842Z",
|
||||
"generatedAt": "2026-05-29T20:59:56.115Z",
|
||||
"locale": "pt-BR",
|
||||
"model": "gpt-5.5",
|
||||
"provider": "openai",
|
||||
"sourceHash": "1ddd9306566b37255394133ec48e05dfe9dd502fd57a34d3c9c96152331730e1",
|
||||
"totalKeys": 1269,
|
||||
"translatedKeys": 1269,
|
||||
"sourceHash": "d51c5347e718c0fbe7353fccf82cefc0ef09b95b25acbc840a2f19b0c17b9296",
|
||||
"totalKeys": 1271,
|
||||
"translatedKeys": 1271,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
2
ui/src/i18n/.i18n/pt-BR.tm.jsonl
generated
Normal file
2
ui/src/i18n/.i18n/pt-BR.tm.jsonl
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
{"cache_key":"0ec17d42d507c693995aa202c0aa2c38d62fb7aa0c0663165a00896f206b352d","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goal","source_path":"ui/src/i18n/locales/pt-BR.ts","src_lang":"en","text":"Goal","text_hash":"cdbf6975e8a35b0d03558be6822dfae166482c24fb86b0433f60e8167f5c91e4","tgt_lang":"pt-BR","translated":"Objetivo","updated_at":"2026-05-29T20:59:56.109Z"}
|
||||
{"cache_key":"3d4dcc4505a4934b851fcc18f8b4c646a4f02c41d5117c454c501a37f3004697","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goalNote","source_path":"ui/src/i18n/locales/pt-BR.ts","src_lang":"en","text":"Goal note","text_hash":"1afb7855a394ef7078728de1c804d6b995413db4eafe7d74190076cb9ed2c9f5","tgt_lang":"pt-BR","translated":"Nota do objetivo","updated_at":"2026-05-29T20:59:56.109Z"}
|
||||
8
ui/src/i18n/.i18n/th.meta.json
generated
8
ui/src/i18n/.i18n/th.meta.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-29T18:10:16.172Z",
|
||||
"generatedAt": "2026-05-29T21:02:14.037Z",
|
||||
"locale": "th",
|
||||
"model": "gpt-5.5",
|
||||
"provider": "openai",
|
||||
"sourceHash": "1ddd9306566b37255394133ec48e05dfe9dd502fd57a34d3c9c96152331730e1",
|
||||
"totalKeys": 1269,
|
||||
"translatedKeys": 1269,
|
||||
"sourceHash": "d51c5347e718c0fbe7353fccf82cefc0ef09b95b25acbc840a2f19b0c17b9296",
|
||||
"totalKeys": 1271,
|
||||
"translatedKeys": 1271,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
2
ui/src/i18n/.i18n/th.tm.jsonl
generated
Normal file
2
ui/src/i18n/.i18n/th.tm.jsonl
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
{"cache_key":"6d49f61e8daf29818cae027ae36136a2ebff9dc4e819e22d841c22f70ffae4cf","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goal","source_path":"ui/src/i18n/locales/th.ts","src_lang":"en","text":"Goal","text_hash":"cdbf6975e8a35b0d03558be6822dfae166482c24fb86b0433f60e8167f5c91e4","tgt_lang":"th","translated":"เป้าหมาย","updated_at":"2026-05-29T21:02:14.030Z"}
|
||||
{"cache_key":"ea705a9b3cb50a40d6d20d46fb239b99b37ae3745afebcef9150ae0922120b9f","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goalNote","source_path":"ui/src/i18n/locales/th.ts","src_lang":"en","text":"Goal note","text_hash":"1afb7855a394ef7078728de1c804d6b995413db4eafe7d74190076cb9ed2c9f5","tgt_lang":"th","translated":"หมายเหตุเป้าหมาย","updated_at":"2026-05-29T21:02:14.030Z"}
|
||||
8
ui/src/i18n/.i18n/tr.meta.json
generated
8
ui/src/i18n/.i18n/tr.meta.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-29T18:10:10.102Z",
|
||||
"generatedAt": "2026-05-29T21:01:26.035Z",
|
||||
"locale": "tr",
|
||||
"model": "gpt-5.5",
|
||||
"provider": "openai",
|
||||
"sourceHash": "1ddd9306566b37255394133ec48e05dfe9dd502fd57a34d3c9c96152331730e1",
|
||||
"totalKeys": 1269,
|
||||
"translatedKeys": 1269,
|
||||
"sourceHash": "d51c5347e718c0fbe7353fccf82cefc0ef09b95b25acbc840a2f19b0c17b9296",
|
||||
"totalKeys": 1271,
|
||||
"translatedKeys": 1271,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
2
ui/src/i18n/.i18n/tr.tm.jsonl
generated
Normal file
2
ui/src/i18n/.i18n/tr.tm.jsonl
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
{"cache_key":"2425436cbd6df1e63399e29ead2cc39b64f74162add08ff66575a408f31b6868","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goalNote","source_path":"ui/src/i18n/locales/tr.ts","src_lang":"en","text":"Goal note","text_hash":"1afb7855a394ef7078728de1c804d6b995413db4eafe7d74190076cb9ed2c9f5","tgt_lang":"tr","translated":"Hedef notu","updated_at":"2026-05-29T21:01:25.865Z"}
|
||||
{"cache_key":"89dc18d52451cd19d966cefb92e113df76f096c54ef2c6c44a745639484261be","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goal","source_path":"ui/src/i18n/locales/tr.ts","src_lang":"en","text":"Goal","text_hash":"cdbf6975e8a35b0d03558be6822dfae166482c24fb86b0433f60e8167f5c91e4","tgt_lang":"tr","translated":"Hedef","updated_at":"2026-05-29T21:01:25.865Z"}
|
||||
8
ui/src/i18n/.i18n/uk.meta.json
generated
8
ui/src/i18n/.i18n/uk.meta.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-29T18:10:11.525Z",
|
||||
"generatedAt": "2026-05-29T21:01:33.693Z",
|
||||
"locale": "uk",
|
||||
"model": "gpt-5.5",
|
||||
"provider": "openai",
|
||||
"sourceHash": "1ddd9306566b37255394133ec48e05dfe9dd502fd57a34d3c9c96152331730e1",
|
||||
"totalKeys": 1269,
|
||||
"translatedKeys": 1269,
|
||||
"sourceHash": "d51c5347e718c0fbe7353fccf82cefc0ef09b95b25acbc840a2f19b0c17b9296",
|
||||
"totalKeys": 1271,
|
||||
"translatedKeys": 1271,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
2
ui/src/i18n/.i18n/uk.tm.jsonl
generated
Normal file
2
ui/src/i18n/.i18n/uk.tm.jsonl
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
{"cache_key":"10837b29e8b086f86bfe06bb6fe52ff30536ac329d213f4fd6efddf12ac6c18d","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goalNote","source_path":"ui/src/i18n/locales/uk.ts","src_lang":"en","text":"Goal note","text_hash":"1afb7855a394ef7078728de1c804d6b995413db4eafe7d74190076cb9ed2c9f5","tgt_lang":"uk","translated":"Примітка до мети","updated_at":"2026-05-29T21:01:33.548Z"}
|
||||
{"cache_key":"a7d851a926019e2bd61845a835cdb17a9a048573abc8e000276b4c4f79fe979d","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goal","source_path":"ui/src/i18n/locales/uk.ts","src_lang":"en","text":"Goal","text_hash":"cdbf6975e8a35b0d03558be6822dfae166482c24fb86b0433f60e8167f5c91e4","tgt_lang":"uk","translated":"Мета","updated_at":"2026-05-29T21:01:33.548Z"}
|
||||
8
ui/src/i18n/.i18n/vi.meta.json
generated
8
ui/src/i18n/.i18n/vi.meta.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-29T18:10:17.905Z",
|
||||
"generatedAt": "2026-05-29T21:02:23.397Z",
|
||||
"locale": "vi",
|
||||
"model": "gpt-5.5",
|
||||
"provider": "openai",
|
||||
"sourceHash": "1ddd9306566b37255394133ec48e05dfe9dd502fd57a34d3c9c96152331730e1",
|
||||
"totalKeys": 1269,
|
||||
"translatedKeys": 1269,
|
||||
"sourceHash": "d51c5347e718c0fbe7353fccf82cefc0ef09b95b25acbc840a2f19b0c17b9296",
|
||||
"totalKeys": 1271,
|
||||
"translatedKeys": 1271,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
2
ui/src/i18n/.i18n/vi.tm.jsonl
generated
Normal file
2
ui/src/i18n/.i18n/vi.tm.jsonl
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
{"cache_key":"37e1b972340d8c2b5d14328166f22dff7683c48e7e618680b94cf4a5846e3395","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goalNote","source_path":"ui/src/i18n/locales/vi.ts","src_lang":"en","text":"Goal note","text_hash":"1afb7855a394ef7078728de1c804d6b995413db4eafe7d74190076cb9ed2c9f5","tgt_lang":"vi","translated":"Ghi chú mục tiêu","updated_at":"2026-05-29T21:02:23.226Z"}
|
||||
{"cache_key":"687bcca75327bc0dfd9262a5ad1ae85b09522d2ad3e1b408819c9c6ac632aeab","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goal","source_path":"ui/src/i18n/locales/vi.ts","src_lang":"en","text":"Goal","text_hash":"cdbf6975e8a35b0d03558be6822dfae166482c24fb86b0433f60e8167f5c91e4","tgt_lang":"vi","translated":"Mục tiêu","updated_at":"2026-05-29T21:02:23.226Z"}
|
||||
8
ui/src/i18n/.i18n/zh-CN.meta.json
generated
8
ui/src/i18n/.i18n/zh-CN.meta.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-29T18:09:54.364Z",
|
||||
"generatedAt": "2026-05-29T20:59:45.228Z",
|
||||
"locale": "zh-CN",
|
||||
"model": "gpt-5.5",
|
||||
"provider": "openai",
|
||||
"sourceHash": "1ddd9306566b37255394133ec48e05dfe9dd502fd57a34d3c9c96152331730e1",
|
||||
"totalKeys": 1269,
|
||||
"translatedKeys": 1269,
|
||||
"sourceHash": "d51c5347e718c0fbe7353fccf82cefc0ef09b95b25acbc840a2f19b0c17b9296",
|
||||
"totalKeys": 1271,
|
||||
"translatedKeys": 1271,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
2
ui/src/i18n/.i18n/zh-CN.tm.jsonl
generated
Normal file
2
ui/src/i18n/.i18n/zh-CN.tm.jsonl
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
{"cache_key":"09ed15bb12e7e25d15337bb2d8b7e301d3838eea96f0fdc749c73cbb6e734b1f","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goal","source_path":"ui/src/i18n/locales/zh-CN.ts","src_lang":"en","text":"Goal","text_hash":"cdbf6975e8a35b0d03558be6822dfae166482c24fb86b0433f60e8167f5c91e4","tgt_lang":"zh-CN","translated":"目标","updated_at":"2026-05-29T20:59:45.212Z"}
|
||||
{"cache_key":"a22243320fa56f3a951b9a6a9a033efaee999643c0de514174713dbc0d6983b9","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goalNote","source_path":"ui/src/i18n/locales/zh-CN.ts","src_lang":"en","text":"Goal note","text_hash":"1afb7855a394ef7078728de1c804d6b995413db4eafe7d74190076cb9ed2c9f5","tgt_lang":"zh-CN","translated":"目标备注","updated_at":"2026-05-29T20:59:45.213Z"}
|
||||
8
ui/src/i18n/.i18n/zh-TW.meta.json
generated
8
ui/src/i18n/.i18n/zh-TW.meta.json
generated
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"fallbackKeys": [],
|
||||
"generatedAt": "2026-05-29T18:09:55.672Z",
|
||||
"generatedAt": "2026-05-29T20:59:52.034Z",
|
||||
"locale": "zh-TW",
|
||||
"model": "gpt-5.5",
|
||||
"provider": "openai",
|
||||
"sourceHash": "1ddd9306566b37255394133ec48e05dfe9dd502fd57a34d3c9c96152331730e1",
|
||||
"totalKeys": 1269,
|
||||
"translatedKeys": 1269,
|
||||
"sourceHash": "d51c5347e718c0fbe7353fccf82cefc0ef09b95b25acbc840a2f19b0c17b9296",
|
||||
"totalKeys": 1271,
|
||||
"translatedKeys": 1271,
|
||||
"workflow": 1
|
||||
}
|
||||
|
||||
2
ui/src/i18n/.i18n/zh-TW.tm.jsonl
generated
Normal file
2
ui/src/i18n/.i18n/zh-TW.tm.jsonl
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
{"cache_key":"9979d886403e24df60997801e043da82148173019f9df8141fce453200283ac0","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goalNote","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Goal note","text_hash":"1afb7855a394ef7078728de1c804d6b995413db4eafe7d74190076cb9ed2c9f5","tgt_lang":"zh-TW","translated":"目標備註","updated_at":"2026-05-29T20:59:51.845Z"}
|
||||
{"cache_key":"d4a6c06bfdfedcffd1b6e7ad155ea4138c82ffa07146a06a5747b8b69310a82d","model":"gpt-5.5","provider":"openai","segment_id":"sessionsView.goal","source_path":"ui/src/i18n/locales/zh-TW.ts","src_lang":"en","text":"Goal","text_hash":"cdbf6975e8a35b0d03558be6822dfae166482c24fb86b0433f60e8167f5c91e4","tgt_lang":"zh-TW","translated":"目標","updated_at":"2026-05-29T20:59:51.845Z"}
|
||||
2
ui/src/i18n/locales/ar.ts
generated
2
ui/src/i18n/locales/ar.ts
generated
@@ -184,6 +184,8 @@ export const ar: TranslationMap = {
|
||||
updated: "تم التحديث",
|
||||
tokens: "الرموز",
|
||||
compaction: "الضغط",
|
||||
goal: "الهدف",
|
||||
goalNote: "ملاحظة الهدف",
|
||||
thinking: "التفكير",
|
||||
fast: "سريع",
|
||||
verbose: "مطوّل",
|
||||
|
||||
2
ui/src/i18n/locales/de.ts
generated
2
ui/src/i18n/locales/de.ts
generated
@@ -188,6 +188,8 @@ export const de: TranslationMap = {
|
||||
updated: "Aktualisiert",
|
||||
tokens: "Tokens",
|
||||
compaction: "Kompaktierung",
|
||||
goal: "Ziel",
|
||||
goalNote: "Zielnotiz",
|
||||
thinking: "Denken",
|
||||
fast: "Schnell",
|
||||
verbose: "Ausführlich",
|
||||
|
||||
@@ -183,6 +183,8 @@ export const en: TranslationMap = {
|
||||
updated: "Updated",
|
||||
tokens: "Tokens",
|
||||
compaction: "Compaction",
|
||||
goal: "Goal",
|
||||
goalNote: "Goal note",
|
||||
thinking: "Thinking",
|
||||
fast: "Fast",
|
||||
verbose: "Verbose",
|
||||
|
||||
2
ui/src/i18n/locales/es.ts
generated
2
ui/src/i18n/locales/es.ts
generated
@@ -185,6 +185,8 @@ export const es: TranslationMap = {
|
||||
updated: "Actualizado",
|
||||
tokens: "Tokens",
|
||||
compaction: "Compactación",
|
||||
goal: "Objetivo",
|
||||
goalNote: "Nota del objetivo",
|
||||
thinking: "Pensamiento",
|
||||
fast: "Rápido",
|
||||
verbose: "Detallado",
|
||||
|
||||
2
ui/src/i18n/locales/fa.ts
generated
2
ui/src/i18n/locales/fa.ts
generated
@@ -186,6 +186,8 @@ export const fa: TranslationMap = {
|
||||
updated: "بهروزشده",
|
||||
tokens: "توکنها",
|
||||
compaction: "فشردهسازی",
|
||||
goal: "هدف",
|
||||
goalNote: "یادداشت هدف",
|
||||
thinking: "تفکر",
|
||||
fast: "سریع",
|
||||
verbose: "پرگویی",
|
||||
|
||||
2
ui/src/i18n/locales/fr.ts
generated
2
ui/src/i18n/locales/fr.ts
generated
@@ -187,6 +187,8 @@ export const fr: TranslationMap = {
|
||||
updated: "Mis à jour",
|
||||
tokens: "Jetons",
|
||||
compaction: "Compactage",
|
||||
goal: "Objectif",
|
||||
goalNote: "Note d’objectif",
|
||||
thinking: "Réflexion",
|
||||
fast: "Rapide",
|
||||
verbose: "Détaillé",
|
||||
|
||||
2
ui/src/i18n/locales/id.ts
generated
2
ui/src/i18n/locales/id.ts
generated
@@ -185,6 +185,8 @@ export const id: TranslationMap = {
|
||||
updated: "Diperbarui",
|
||||
tokens: "Token",
|
||||
compaction: "Pemadatan",
|
||||
goal: "Tujuan",
|
||||
goalNote: "Catatan tujuan",
|
||||
thinking: "Thinking",
|
||||
fast: "Cepat",
|
||||
verbose: "Verbose",
|
||||
|
||||
2
ui/src/i18n/locales/it.ts
generated
2
ui/src/i18n/locales/it.ts
generated
@@ -185,6 +185,8 @@ export const it: TranslationMap = {
|
||||
updated: "Aggiornato",
|
||||
tokens: "Token",
|
||||
compaction: "Compattazione",
|
||||
goal: "Obiettivo",
|
||||
goalNote: "Nota sull'obiettivo",
|
||||
thinking: "Thinking",
|
||||
fast: "Veloce",
|
||||
verbose: "Dettagliato",
|
||||
|
||||
2
ui/src/i18n/locales/ja-JP.ts
generated
2
ui/src/i18n/locales/ja-JP.ts
generated
@@ -188,6 +188,8 @@ export const ja_JP: TranslationMap = {
|
||||
updated: "更新日時",
|
||||
tokens: "トークン",
|
||||
compaction: "圧縮",
|
||||
goal: "目標",
|
||||
goalNote: "目標メモ",
|
||||
thinking: "Thinking",
|
||||
fast: "高速",
|
||||
verbose: "詳細",
|
||||
|
||||
2
ui/src/i18n/locales/ko.ts
generated
2
ui/src/i18n/locales/ko.ts
generated
@@ -184,6 +184,8 @@ export const ko: TranslationMap = {
|
||||
updated: "업데이트됨",
|
||||
tokens: "토큰",
|
||||
compaction: "압축",
|
||||
goal: "목표",
|
||||
goalNote: "목표 메모",
|
||||
thinking: "생각 수준",
|
||||
fast: "빠름",
|
||||
verbose: "상세",
|
||||
|
||||
2
ui/src/i18n/locales/nl.ts
generated
2
ui/src/i18n/locales/nl.ts
generated
@@ -187,6 +187,8 @@ export const nl: TranslationMap = {
|
||||
updated: "Bijgewerkt",
|
||||
tokens: "Tokens",
|
||||
compaction: "Compactie",
|
||||
goal: "Doel",
|
||||
goalNote: "Doelnotitie",
|
||||
thinking: "Denken",
|
||||
fast: "Snel",
|
||||
verbose: "Uitgebreid",
|
||||
|
||||
2
ui/src/i18n/locales/pl.ts
generated
2
ui/src/i18n/locales/pl.ts
generated
@@ -186,6 +186,8 @@ export const pl: TranslationMap = {
|
||||
updated: "Zaktualizowano",
|
||||
tokens: "Tokeny",
|
||||
compaction: "Kompaktowanie",
|
||||
goal: "Cel",
|
||||
goalNote: "Notatka dotycząca celu",
|
||||
thinking: "Myślenie",
|
||||
fast: "Szybko",
|
||||
verbose: "Szczegółowo",
|
||||
|
||||
2
ui/src/i18n/locales/pt-BR.ts
generated
2
ui/src/i18n/locales/pt-BR.ts
generated
@@ -185,6 +185,8 @@ export const pt_BR: TranslationMap = {
|
||||
updated: "Atualizado",
|
||||
tokens: "Tokens",
|
||||
compaction: "Compactação",
|
||||
goal: "Objetivo",
|
||||
goalNote: "Nota do objetivo",
|
||||
thinking: "Pensamento",
|
||||
fast: "Rápido",
|
||||
verbose: "Detalhado",
|
||||
|
||||
2
ui/src/i18n/locales/th.ts
generated
2
ui/src/i18n/locales/th.ts
generated
@@ -183,6 +183,8 @@ export const th: TranslationMap = {
|
||||
updated: "อัปเดตแล้ว",
|
||||
tokens: "โทเค็น",
|
||||
compaction: "การบีบอัด",
|
||||
goal: "เป้าหมาย",
|
||||
goalNote: "หมายเหตุเป้าหมาย",
|
||||
thinking: "Thinking",
|
||||
fast: "เร็ว",
|
||||
verbose: "ละเอียด",
|
||||
|
||||
2
ui/src/i18n/locales/tr.ts
generated
2
ui/src/i18n/locales/tr.ts
generated
@@ -187,6 +187,8 @@ export const tr: TranslationMap = {
|
||||
updated: "Güncellendi",
|
||||
tokens: "Tokenlar",
|
||||
compaction: "Sıkıştırma",
|
||||
goal: "Hedef",
|
||||
goalNote: "Hedef notu",
|
||||
thinking: "Düşünme",
|
||||
fast: "Hızlı",
|
||||
verbose: "Ayrıntılı",
|
||||
|
||||
2
ui/src/i18n/locales/uk.ts
generated
2
ui/src/i18n/locales/uk.ts
generated
@@ -186,6 +186,8 @@ export const uk: TranslationMap = {
|
||||
updated: "Оновлено",
|
||||
tokens: "Токени",
|
||||
compaction: "Стиснення",
|
||||
goal: "Мета",
|
||||
goalNote: "Примітка до мети",
|
||||
thinking: "Обмірковування",
|
||||
fast: "Швидко",
|
||||
verbose: "Докладно",
|
||||
|
||||
2
ui/src/i18n/locales/vi.ts
generated
2
ui/src/i18n/locales/vi.ts
generated
@@ -185,6 +185,8 @@ export const vi: TranslationMap = {
|
||||
updated: "Đã cập nhật",
|
||||
tokens: "Token",
|
||||
compaction: "Nén",
|
||||
goal: "Mục tiêu",
|
||||
goalNote: "Ghi chú mục tiêu",
|
||||
thinking: "Suy nghĩ",
|
||||
fast: "Nhanh",
|
||||
verbose: "Chi tiết",
|
||||
|
||||
2
ui/src/i18n/locales/zh-CN.ts
generated
2
ui/src/i18n/locales/zh-CN.ts
generated
@@ -183,6 +183,8 @@ export const zh_CN: TranslationMap = {
|
||||
updated: "已更新",
|
||||
tokens: "Token",
|
||||
compaction: "压缩",
|
||||
goal: "目标",
|
||||
goalNote: "目标备注",
|
||||
thinking: "思考",
|
||||
fast: "快速",
|
||||
verbose: "详细",
|
||||
|
||||
2
ui/src/i18n/locales/zh-TW.ts
generated
2
ui/src/i18n/locales/zh-TW.ts
generated
@@ -183,6 +183,8 @@ export const zh_TW: TranslationMap = {
|
||||
updated: "已更新",
|
||||
tokens: "Token",
|
||||
compaction: "壓縮",
|
||||
goal: "目標",
|
||||
goalNote: "目標備註",
|
||||
thinking: "思考",
|
||||
fast: "快速",
|
||||
verbose: "詳細",
|
||||
|
||||
@@ -434,9 +434,9 @@ function sessionDetailItems(params: {
|
||||
};
|
||||
add(t("sessionsView.status"), row.status);
|
||||
if (row.goal) {
|
||||
details.push({ label: "Goal", value: formatGoalDetail(row.goal) });
|
||||
details.push({ label: t("sessionsView.goal"), value: formatGoalDetail(row.goal) });
|
||||
}
|
||||
add("Goal note", row.goal?.lastStatusNote);
|
||||
add(t("sessionsView.goalNote"), row.goal?.lastStatusNote);
|
||||
add(t("sessionsView.model"), row.model);
|
||||
add(t("sessionsView.provider"), row.modelProvider);
|
||||
add(t("sessionsView.runtime"), formatRuntimeMs(row.runtimeMs));
|
||||
|
||||
Reference in New Issue
Block a user