@sema-agent/server 7.89.0 → 7.90.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/boot/fatal-exit.d.ts +3 -0
- package/dist/boot/fatal-exit.js +5 -0
- package/dist/boot/resolve-spec.js +2 -0
- package/dist/brain.js +32 -19
- package/dist/capabilities/memory-notice.d.ts +7 -1
- package/dist/capabilities/memory-notice.js +6 -6
- package/dist/capabilities/team.d.ts +37 -0
- package/dist/config-catalog.js +6 -18
- package/dist/config.d.ts +1 -1
- package/dist/config.js +5 -5
- package/dist/hooks/hook-llm.js +10 -6
- package/dist/hooks/hook-runner.js +19 -2
- package/dist/http/admission.js +9 -5
- package/dist/http/routes/capabilities.js +2 -2
- package/dist/http/routes/session-sync.js +8 -2
- package/dist/http/routes/sessions.js +16 -14
- package/dist/http/wire-types.d.ts +12 -0
- package/dist/main.js +2 -1
- package/dist/model-route-url.d.ts +8 -0
- package/dist/model-route-url.js +17 -8
- package/dist/plugins/memory-embedder.d.ts +0 -6
- package/dist/plugins/memory-embedder.js +10 -16
- package/dist/request-key-closure.js +2 -1
- package/dist/runtime-governance.d.ts +16 -7
- package/dist/runtime-governance.js +27 -7
- package/dist/session-policy-wire.d.ts +103 -0
- package/dist/session-policy-wire.js +66 -0
- package/dist/task-settings.d.ts +12 -2
- package/dist/task-settings.js +7 -5
- package/dist/trace/core-keyset-guard.d.ts +2 -2
- package/dist/trace/project.d.ts +7 -0
- package/dist/trace/project.js +1 -0
- package/dist/trace/redact.d.ts +96 -11
- package/dist/trace/redact.js +65 -10
- package/package.json +2 -2
package/dist/trace/redact.d.ts
CHANGED
|
@@ -141,12 +141,13 @@ export declare function redactedPreview(value: unknown, cap?: number): string |
|
|
|
141
141
|
* (`model-route-url.ts` 第一臂)共用的**同一只**判据:两处曾各判各的,而 `URL.canParse` 单独为真的
|
|
142
142
|
* **不透明** URL 恰好是「凭据位看不见」的那一形。
|
|
143
143
|
*
|
|
144
|
-
* ⚠️ **射程如实**(车JC 复审去幻觉轮:此处原文写的是「本仓唯一判据」,亲读证伪)——
|
|
145
|
-
* 裸 `URL.canParse`
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
144
|
+
* ⚠️ **射程如实**(车JC 复审去幻觉轮:此处原文写的是「本仓唯一判据」,亲读证伪)—— 仓里**另有一处**仍用
|
|
145
|
+
* 裸 `URL.canParse` 回答同一个问题,且**刻意不在射程内**(改它是一次新的行为面拒启,按三问另开车):
|
|
146
|
+
* `config.ts` 的 `GIT_API_BASEURL`(`parseGitApiKind`):无协议闸 ⇒ 不透明形今天过门(拒因经本铸点,
|
|
147
|
+
* 所以不回显凭据;可用性由 `fetch` 那侧决定)。
|
|
148
|
+
* ✅ 原先列在这里的第二处(`config-catalog.ts` 的 `stripUrlUserinfo`:不透明形 `href === 原串` ⇒ 原样回显)
|
|
149
|
+
* 已在 7.90.0 随 core `displayUrlOf` 到货**闭环** —— 那只函数整只退役,url 型行的回显改走本铸点,
|
|
150
|
+
* 于是这道闸对它自动成立(catalog wire 行为面变更,CHANGELOG 已对外写明并点名 @web-admin)。
|
|
150
151
|
*
|
|
151
152
|
* 🔴 谓词写成 `URL.canParse` 而不是 try/catch:这一臂**不是兜底**(兜底才归静默降级那张表管),它是判据本身 ——
|
|
152
153
|
* 「不是一个层级式 URL」是一个可判的状态,用异常当控制流只会让它看起来像一次意外。
|
|
@@ -164,16 +165,100 @@ export declare function isHierarchicalUrl(raw: string): boolean;
|
|
|
164
165
|
* 网关 URL 的 userinfo / query 里,密码明文落 `engine-*.log`(日志的读者面比 run 错误文本更宽:它落盘、
|
|
165
166
|
* 被采集、进工单)。同类出口不止这两处,类级机器门在 `test/config-url-display-safe.test.ts`。
|
|
166
167
|
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
168
|
+
* 🔴 **S-483 展示半场(7.90.0 / core 7.24.0):两条正交的律合成一次,名表的射程缩到不可约的那一半**。
|
|
169
|
+
* 改前内部只有一条:`redactSecrets(new URL(raw).href)` —— 自由文本那条**名单制**的律,query 的值只在键名
|
|
170
|
+
* 落在 `CREDENTIAL_NAME_WORDS`(或值本身像个密钥)时才遮,于是**没人列过的那个键名**就是它的盲点
|
|
171
|
+
* (`?sig=…` / `?auth=…` / 部署自定义的网关参数名)。现在前面先跑 core 的 `displayUrlOf`,它是**位置制**的:
|
|
172
|
+
* · userinfo **落地**(不是遮成记号 —— 它从**解析**组合 `scheme://host + path + query`,userinfo 结构上
|
|
173
|
+
* 进不了产物);凭据在 userinfo 这件事本身仍然响亮,只不过是在**声明关口**响
|
|
174
|
+
* (`modelRouteUrlRefusal` / `parseA2aServe` / core 的 `assertServableRequestRoot`),不靠展示串暗示;
|
|
175
|
+
* · query 的**每一个值**成记号、**键名留着**(键名必须留:两条只在 query 上不同的根若都渲染成同一个串,
|
|
176
|
+
* 「A 配给了 B」这句拒因会写成「targets X … paired with X」);
|
|
177
|
+
* · 无 `=` 的**裸段**整片成一枚记号、连名字都不给(`URLSearchParams` 把它读成「空值的键」,那个读法
|
|
178
|
+
* 正好把 payload 本身放上了屏);
|
|
179
|
+
* · fragment 丢掉(implicit-OAuth 把 bearer 放那儿),尾斜杠与默认端口按配对键的同一把尺折。
|
|
180
|
+
* 位置律跑完**再跑一次形状律**({@link redactSecrets}):它管的是「一段字面量**长得像**凭据」,而位置律
|
|
181
|
+
* 对这件事结构性失明 —— `pathname` 被逐字保留,「这个路径段是不是一枚令牌」只能按形认。两条律正交、
|
|
182
|
+
* 各有一个属主、在铸点里合成一次;因为位置律先跑,名表在 query 上无事可做,它的射程被**缩小**到
|
|
183
|
+
* host + path 这一不可约的半边。
|
|
184
|
+
* 换尺的**代价成文、不是意外**:非凭据参数的值(`?model=x`)现在也成记号 —— 排障读不到它了。方向一致:
|
|
185
|
+
* 一个没人列过的凭据参数名泄出去是不可逆的,少读一个模型名不是。本仓原先**三份**各判各的 URL 脱敏实现
|
|
186
|
+
* (本铸点 / catalog 的 `stripUrlUserinfo` / embedder 的 `redactEndpointForLog`)合成这一只,记号闭形仍归
|
|
187
|
+
* 本模块({@link REDACTION_TOKENS};委托边界的闭集校验在下面的加载期检查)。
|
|
188
|
+
*
|
|
189
|
+
* 本函数自己只剩两件事,两件都是**配置型 URL 特有**的(core 的渲染器对这两形有自己的答案,而配置面要更严):
|
|
169
190
|
* ① **有效性闸**:配置里的那一位应当是一个**绝对 URL**。`new URL(raw)` 解析不动 ⇒ 这一位根本不是 URL
|
|
170
191
|
* (裸 `host:port`、相对路径、手滑的散文),自由文本的 URL 律对它没有定界依据 ⇒ 返回**占位**
|
|
171
192
|
* {@link DISPLAY_SAFE_URL_TOKEN},**不回落原值**([ref]:安全轴上的兜底必须 fail-closed 或响亮)。
|
|
172
193
|
* 成文代价:`MODEL_GATEWAY_BASEURL=127.0.0.1:8000` 这种缺 scheme 的配置在日志上变成 `«redacted:url»`
|
|
173
194
|
* —— 而它在 `fetch` 那一侧本来就是必然失败的形(WHATWG 要求绝对 URL),日志上响亮一点方向一致。
|
|
174
195
|
* ② **空串 = 未设**:里面没有凭据,原样回空串(不铸占位 —— 占位会让「没配」读成「配了但看不见」)。
|
|
175
|
-
*
|
|
176
|
-
*
|
|
196
|
+
* ⚠️ **保形只到 host / port / path 这一层**(S-483 起如实改口;`test/config-url-display-safe.test.ts`
|
|
197
|
+
* 的 S-483-o 是这句话的机器读数,别在散文里再抄一遍):scheme / host / port / path 的字节逐字(模 WHATWG
|
|
198
|
+
* 的 HTTP 等价归一:默认端口省、host 小写、IDN→punycode、点段折叠,再加**整串末尾**的斜杠去掉 ——
|
|
199
|
+
* 是**整串末尾**不是「路径末尾」:`…/p/` ⇒ `…/p`,而 `…/p/?k=v` 的 `/p/` 保留),query 只留**键名**。
|
|
200
|
+
*/
|
|
201
|
+
/** 位置律的**形** —— 生产路径恒是 core 的 {@link displayUrlOf}。参数化是**为可测性开的唯一一个缝**
|
|
202
|
+
* (S-497 的「违反格」要造一个「位置律漏了一段」的世界,而拿上游的现成缺陷当触发器会在它修好那天失效);
|
|
203
|
+
* 生产调用方**不传**这个参数。 */
|
|
204
|
+
export type PositionalUrlLaw = (url: string) => string;
|
|
205
|
+
/** 测试口(与 `observability/fail-open.ts` 的 `resetFailOpenRecorderForTest` 同姿势):一次性 warn 的闸是
|
|
206
|
+
* 模块级的,而「只 warn 一次」本身要被钉,所以给测试一个复位口。生产零调用。 */
|
|
207
|
+
export declare function resetDisplaySafeFailClosedWarnForTest(): void;
|
|
208
|
+
/**
|
|
209
|
+
* 🔴 **S-497 的不变量 —— 形状律在 query / fragment 区间上有一条凭据跨度 ⇒ 那一整段不出门**。
|
|
210
|
+
*
|
|
211
|
+
* 为什么需要它(差分实测 650 形里 26 个反例,本车亲跑复现):两条律**串起来**的时候,前一条会把后一条
|
|
212
|
+
* 赖以认字的**结构**改掉。实测的病形是 `https://h/?next=https://u:&pw@h=` —— 位置律先按 `&` 切段、把
|
|
213
|
+
* `next=https://u:` 整段换成记号,于是 `&` 之后那半截密码落到了**下一段的键名位**上被保留,而形状律
|
|
214
|
+
* 此刻已经看不到 `://<userinfo>@` 这个结构、无从下手(`redactSecrets(原串)` 是对的,可它拿不到原串)。
|
|
215
|
+
*
|
|
216
|
+
* 🔴 **判据落在「切分之前」,而不是在产物文本里找证据**(这一条是本不变量的全部)。三次尝试都被同一件事
|
|
217
|
+
* 击穿:①凭据**整段**不得是产物的子串 —— 位置律把它切开了,整段自然不是子串;②切成**片** + 长度下限 ——
|
|
218
|
+
* 材料能被切成全是单字符(`&a!b!c!d@`)甚至纯标点(`&!$()*+,;@`),任何下限都漏;③**字符集**包含 ——
|
|
219
|
+
* 密码的每个字符都能在同一条 URL 的 path / host / 别的键名里另外出现一次,包含关系成立而明文骑在产物上
|
|
220
|
+
* (`https://h/abcdefghijklmnopqrstuvwxyz0123456789?ok=1&next=https://u:&secret123=x@h`,S-497-k)。
|
|
221
|
+
* 共同的根:**切开之后,「这个字节来自凭据」在文本上不可判**。⇒ 判据搬到切分之前,用形状律自己报的
|
|
222
|
+
* **跨度坐标**当出处证明({@link shapeLawCredentialInQueryOrFragment})—— 不需要长度下限、字符类白名单
|
|
223
|
+
* 或任何集合运算,**规则比前三版都少**,而且对「将来出现的新形」天然封闭(新形状照样落在跨度里)。
|
|
224
|
+
*
|
|
225
|
+
* 两步(一条规则,不按形状分臂):
|
|
226
|
+
* ① 形状律在**归一化视图**上取跨度;凡有一条跨度落进 `?`(或 `#`)之后的区间 ⇒ 整段 query + fragment
|
|
227
|
+
* 换一枚 {@link REDACTION_TOKENS} 内的占位,`scheme://host[:port]/path` 留着并照旧过一遍形状律;
|
|
228
|
+
* ② 否则照旧合成:位置律 → 形状律。
|
|
229
|
+
* **过度遮的射程如实**:一条 query 里**真的**带凭据的配置型 URL,自本版起读不到它的**其它**参数名了
|
|
230
|
+
* (改前是逐参数遮)。方向一致,而且干净的根**零影响** —— Azure 式 `?api-version=…&deployment=…` 上
|
|
231
|
+
* 形状律一条跨度都没有,结构上走不到这一步(S-497-e 单独钉住)。path 区间的凭据**不**走这条路:位置律
|
|
232
|
+
* 逐字保留 `pathname`,形状律在产物上照常认得它(S-497-f 的并集格)。
|
|
233
|
+
* 🔴 这是 fail-**closed**(扣下信息),**不**登记 `recordFailOpen` —— 那张表记的是「放行了本该拒的」。
|
|
234
|
+
* 只打一条一次性 warn(**不含原文**:原文就是那段凭据)。
|
|
235
|
+
*/
|
|
236
|
+
export declare function buildDisplaySafeUrl(raw: string, positionalLaw?: PositionalUrlLaw): string;
|
|
237
|
+
/**
|
|
238
|
+
* {@link buildDisplaySafeUrl} 的**带判据**出口:除了展示串,还说这一次有没有落到 fail-closed
|
|
239
|
+
* (= 「query / fragment 因为里面有一条凭据跨度而整段被扣下」)。
|
|
240
|
+
*
|
|
241
|
+
* 🔴 **为什么要这个出口**(发前复审):core 的**路由拒因**是一句**自由文本**,里面嵌着 core **已经用
|
|
242
|
+
* 位置律改写过**的那条 URL —— 到我们手上时 `://<userinfo>@` 的结构早没了,形状律无从下手,于是那条出口
|
|
243
|
+
* 整个在不变量之外(实测:降级拒因把密码写进 `listening` 行)。这里不去猜怎么把上游那句话重新脱敏
|
|
244
|
+
* (那会变成第二个写者),而是用**同一条判决**:铸点对这条 URL 都已经守不住了,任何**引用它**的自由文本
|
|
245
|
+
* 一律扣下(见 {@link buildDisplaySafeRefusal})。判决属主还是这一只函数。
|
|
246
|
+
*/
|
|
247
|
+
export declare function buildDisplaySafeUrlChecked(raw: string, positionalLaw?: PositionalUrlLaw): {
|
|
248
|
+
text: string;
|
|
249
|
+
failedClosed: boolean;
|
|
250
|
+
};
|
|
251
|
+
/**
|
|
252
|
+
* 🔴 **引用了一条「铸点都守不住」的 URL 的自由文本,一律扣下**(S-497 的第二个出口;codex 第三轮 [high] 二)。
|
|
253
|
+
*
|
|
254
|
+
* 料 = 上游(core)的路由拒因:一句自由文本,里面嵌着 core **已经用位置律改写过**的那条 URL。到本仓手上时
|
|
255
|
+
* `://<userinfo>@` 的结构已经没了 ⇒ 形状律认不出那一段 ⇒ 光过一遍 `redactSecrets` 兜不住(实测:降级拒因
|
|
256
|
+
* 把密码写进 `listening` 行)。
|
|
257
|
+
*
|
|
258
|
+
* 判据**不新造**:问 {@link buildDisplaySafeUrlChecked} —— 如果铸点对这几条源 URL 里的任何一条都已经落到
|
|
259
|
+
* fail-closed,那么**任何引用它的句子**都不许出门,换一句固定文案(零原文)。机器可读的那一半不丢:调用方
|
|
260
|
+
* 手里有 `verdict.code`(拒因的码),它本来就在同一个投影的**另一个字段**上。
|
|
261
|
+
* 都守得住 ⇒ 照旧只过一遍形状律(上游的位置律已经把 URL 的凭据位处理过,这一层管的是**形状**那一半)。
|
|
177
262
|
*/
|
|
178
|
-
export declare function
|
|
263
|
+
export declare function buildDisplaySafeRefusal(text: string, sourceUrls: readonly string[]): string;
|
|
179
264
|
//# sourceMappingURL=redact.d.ts.map
|
package/dist/trace/redact.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { stripFormatCharacters } from "@sema-agent/core";
|
|
1
|
+
import { displayUrlOf, stripFormatCharacters } from "@sema-agent/core";
|
|
2
2
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
3
3
|
export const normalizeForRedaction = stripFormatCharacters;
|
|
4
4
|
const REDACTION_TOKEN_RE_SRC = String.raw `«redacted(?::[a-z-]{1,16}){0,2}»`;
|
|
@@ -1085,6 +1085,7 @@ const FRAGMENT_SECRET_PATTERNS = [
|
|
|
1085
1085
|
[valueArm("xapp-\\d-", "[A-Za-z0-9-]", 4, "needs-left-boundary"), "«redacted:slack-token:partial»"],
|
|
1086
1086
|
];
|
|
1087
1087
|
const DISPLAY_SAFE_URL_TOKEN = "«redacted:url»";
|
|
1088
|
+
const WITHHELD_VALUE_TOKEN = "«redacted»";
|
|
1088
1089
|
const REDACTION_TOKENS = new Set([...FULL_ARM_PATTERNS.map(([, r]) => r), PEM_FRAGMENT_TOKEN, DISPLAY_SAFE_URL_TOKEN, ...FRAGMENT_SECRET_PATTERNS.map(([, r]) => r)]
|
|
1089
1090
|
.flatMap((r) => r.match(/«[^»]*»/g) ?? []));
|
|
1090
1091
|
{
|
|
@@ -1092,6 +1093,22 @@ const REDACTION_TOKENS = new Set([...FULL_ARM_PATTERNS.map(([, r]) => r), PEM_FR
|
|
|
1092
1093
|
for (const t of REDACTION_TOKENS)
|
|
1093
1094
|
if (!closed.test(t))
|
|
1094
1095
|
throw new Error(`redact: token ${t} is outside the closed form ${REDACTION_TOKEN_RE_SRC}`);
|
|
1096
|
+
if (!REDACTION_TOKENS.has(WITHHELD_VALUE_TOKEN)) {
|
|
1097
|
+
throw new Error(`redact: ${WITHHELD_VALUE_TOKEN} is no longer produced by the arm tables — the fail-closed filler must stay a closed-set member`);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
{
|
|
1101
|
+
for (const probe of ["https://probe.invalid/p?k=v", "https://probe.invalid/p?bare"]) {
|
|
1102
|
+
const toks = displayUrlOf(probe).match(/«[^»]*»/g) ?? [];
|
|
1103
|
+
if (toks.length === 0) {
|
|
1104
|
+
throw new Error(`redact: core displayUrlOf(${probe}) minted no «…» token at all — upstream changed the placeholder spelling; map it at the delegation boundary (never add a second token table)`);
|
|
1105
|
+
}
|
|
1106
|
+
for (const tok of toks) {
|
|
1107
|
+
if (!REDACTION_TOKENS.has(tok)) {
|
|
1108
|
+
throw new Error(`redact: core displayUrlOf minted ${tok}, which is outside this module's token closed set — map it at the delegation boundary (never add a second token table)`);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1095
1112
|
}
|
|
1096
1113
|
const labelOfToken = (token) => token.replace(/[«»]/g, "").replace(/^redacted:/, "");
|
|
1097
1114
|
const FRAGMENT_ARM_TOKENS = new Set(FRAGMENT_SECRET_PATTERNS.map(([, repl]) => repl));
|
|
@@ -1598,7 +1615,7 @@ const armPool = (() => {
|
|
|
1598
1615
|
const kept = resolveSpanOverlaps(spans);
|
|
1599
1616
|
const out = applySpans(text, kept).out;
|
|
1600
1617
|
if (!wantCount || kept.length === 0)
|
|
1601
|
-
return { out, hits: NO_HITS };
|
|
1618
|
+
return { out, hits: NO_HITS, kept };
|
|
1602
1619
|
const tokenOf = (sp) => members[sp.order].token;
|
|
1603
1620
|
const strong = spans.filter((sp) => armOfToken(tokenOf(sp)) === "full").sort((a, b) => a.start - b.start);
|
|
1604
1621
|
const byArmedToken = new Map();
|
|
@@ -1618,7 +1635,7 @@ const armPool = (() => {
|
|
|
1618
1635
|
else
|
|
1619
1636
|
cur.count += 1;
|
|
1620
1637
|
}
|
|
1621
|
-
return { out, hits: [...byArmedToken.values()].map(({ token, arm, count }) => ({ label: labelOfToken(token), count, arm })) };
|
|
1638
|
+
return { out, hits: [...byArmedToken.values()].map(({ token, arm, count }) => ({ label: labelOfToken(token), count, arm })), kept };
|
|
1622
1639
|
};
|
|
1623
1640
|
})();
|
|
1624
1641
|
let onRedaction;
|
|
@@ -1641,11 +1658,12 @@ function reportHits(hits) {
|
|
|
1641
1658
|
}
|
|
1642
1659
|
function runRedaction(text, observe, json, wantHits) {
|
|
1643
1660
|
const wantCount = wantHits || (onRedaction !== undefined && observe);
|
|
1644
|
-
const
|
|
1661
|
+
const normalized = normalizeForRedaction(text);
|
|
1662
|
+
const r = applyRules(normalized, wantCount, json, observe);
|
|
1645
1663
|
forgetEscapedSeparatorTails();
|
|
1646
1664
|
if (observe)
|
|
1647
1665
|
reportHits(r.hits);
|
|
1648
|
-
return { text: r.out, hits: r.hits };
|
|
1666
|
+
return { text: r.out, hits: r.hits, spans: r.kept };
|
|
1649
1667
|
}
|
|
1650
1668
|
export function redactSecrets(text, opts) {
|
|
1651
1669
|
return runRedaction(text, opts?.observe ?? true, opts?.json === true, false).text;
|
|
@@ -1718,12 +1736,49 @@ export function isHierarchicalUrl(raw) {
|
|
|
1718
1736
|
const u = new URL(raw);
|
|
1719
1737
|
return u.href.startsWith(`${u.protocol}//`);
|
|
1720
1738
|
}
|
|
1721
|
-
|
|
1739
|
+
function shapeLawCredentialInQueryOrFragment(canonical) {
|
|
1740
|
+
const normalized = normalizeForRedaction(canonical);
|
|
1741
|
+
const boundary = normalized.indexOf("?");
|
|
1742
|
+
if (boundary < 0)
|
|
1743
|
+
return false;
|
|
1744
|
+
return runRedaction(canonical, false, false, false).spans.some((sp) => sp.end > boundary);
|
|
1745
|
+
}
|
|
1746
|
+
let displaySafeFailClosedWarned = false;
|
|
1747
|
+
export function resetDisplaySafeFailClosedWarnForTest() {
|
|
1748
|
+
displaySafeFailClosedWarned = false;
|
|
1749
|
+
}
|
|
1750
|
+
export function buildDisplaySafeUrl(raw, positionalLaw = displayUrlOf) {
|
|
1751
|
+
return buildDisplaySafeUrlChecked(raw, positionalLaw).text;
|
|
1752
|
+
}
|
|
1753
|
+
export function buildDisplaySafeUrlChecked(raw, positionalLaw = displayUrlOf) {
|
|
1722
1754
|
if (raw === "")
|
|
1723
|
-
return "";
|
|
1755
|
+
return { text: "", failedClosed: false };
|
|
1724
1756
|
if (!isHierarchicalUrl(raw))
|
|
1725
|
-
return DISPLAY_SAFE_URL_TOKEN;
|
|
1726
|
-
const
|
|
1727
|
-
|
|
1757
|
+
return { text: DISPLAY_SAFE_URL_TOKEN, failedClosed: false };
|
|
1758
|
+
const parsed = new URL(raw);
|
|
1759
|
+
if (shapeLawCredentialInQueryOrFragment(parsed.href)) {
|
|
1760
|
+
noteDisplaySafeFailClosed("query-and-fragment-withheld");
|
|
1761
|
+
const root = redactSecrets(`${parsed.protocol}//${parsed.host}${parsed.pathname}`).replace(/\/+$/, "");
|
|
1762
|
+
return { text: `${root}?${WITHHELD_VALUE_TOKEN}`, failedClosed: true };
|
|
1763
|
+
}
|
|
1764
|
+
return { text: redactSecrets(positionalLaw(raw)), failedClosed: false };
|
|
1765
|
+
}
|
|
1766
|
+
export function buildDisplaySafeRefusal(text, sourceUrls) {
|
|
1767
|
+
if (text === "")
|
|
1768
|
+
return text;
|
|
1769
|
+
if (sourceUrls.some((url) => url !== "" && buildDisplaySafeUrlChecked(url).failedClosed)) {
|
|
1770
|
+
noteDisplaySafeFailClosed("refusal-text-withheld");
|
|
1771
|
+
return REFUSAL_WITHHELD_TEXT;
|
|
1772
|
+
}
|
|
1773
|
+
return redactSecrets(text);
|
|
1774
|
+
}
|
|
1775
|
+
const REFUSAL_WITHHELD_TEXT = "[route refusal text withheld: it quotes a configured URL that carries a credential, and the upstream wording cannot be re-redacted safely once the URL has been rewritten for display. " +
|
|
1776
|
+
"The machine-readable verdict is on the pairing field beside this one; fix the configuration so the credential is not in the URL.]";
|
|
1777
|
+
function noteDisplaySafeFailClosed(level) {
|
|
1778
|
+
if (displaySafeFailClosedWarned)
|
|
1779
|
+
return;
|
|
1780
|
+
displaySafeFailClosedWarned = true;
|
|
1781
|
+
process.emitWarning(`display-safe URL composition fell back to fail-closed (${level}): the positional law reshaped the input so that a fragment the shape law had judged a credential survived into the product. ` +
|
|
1782
|
+
`The product is safe (the offending part is withheld), but this deployment has an input hitting the composition seam — report it upstream. No original bytes are logged.`, "SemaRedactionFailClosed");
|
|
1728
1783
|
}
|
|
1729
1784
|
//# sourceMappingURL=redact.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.90.0",
|
|
4
4
|
"description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BUSL-1.1",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"build:binary:run-local:darwin-arm64": "bun build --compile --target=bun-darwin-arm64 src/run-local.ts --outfile dist/run-local-darwin-arm64"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@sema-agent/core": "7.
|
|
62
|
+
"@sema-agent/core": "7.24.2",
|
|
63
63
|
"@sema-agent/settings-schema": "3.0.0",
|
|
64
64
|
"e2b": "^2.28.0",
|
|
65
65
|
"libsodium-wrappers": "^0.8.4",
|