@sema-agent/server 7.73.1 → 7.75.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/USAGE.md +8 -0
- package/dist/boot/runner-deps.d.ts +0 -4
- package/dist/boot/runner-deps.js +1 -2
- package/dist/boot/session-faces.js +0 -2
- package/dist/boot/stores.js +3 -16
- package/dist/config-types.d.ts +1 -1
- package/dist/deployment-governance.d.ts +23 -3
- package/dist/deployment-governance.js +23 -2
- package/dist/env-facts.d.ts +8 -1
- package/dist/http/route-ctx.d.ts +9 -8
- package/dist/http/routes/approvals-assistant.js +2 -2
- package/dist/http/routes/runs.js +1 -1
- package/dist/http/routes/workflows.js +4 -0
- package/dist/http/server.js +4 -3
- package/dist/main.js +0 -1
- package/dist/observability/fail-open.d.ts +2 -2
- package/dist/observability/fail-open.js +2 -2
- package/dist/observability/run-terminal-log.d.ts +2 -1
- package/dist/orchestration/workflow-agent-session-index.d.ts +41 -83
- package/dist/orchestration/workflow-agent-session-index.js +14 -33
- package/dist/parked-decide.d.ts +30 -33
- package/dist/parked-decide.js +11 -13
- package/dist/plugins/approval-ask-store-sql.js +1 -1
- package/dist/plugins/blob-backend.d.ts +6 -7
- package/dist/plugins/checkpoint-store-sql.d.ts +13 -10
- package/dist/plugins/checkpoint-store-sql.js +6 -24
- package/dist/plugins/file-run-store.d.ts +18 -0
- package/dist/plugins/file-run-store.js +27 -0
- package/dist/plugins/one-time-migrations.d.ts +66 -0
- package/dist/plugins/one-time-migrations.js +193 -0
- package/dist/plugins/pg-pool.d.ts +6 -0
- package/dist/plugins/pg-pool.js +5 -14
- package/dist/plugins/retention-store-sql.js +0 -1
- package/dist/plugins/store-backend.d.ts +11 -15
- package/dist/plugins/store-backend.js +7 -26
- package/dist/plugins/task-attachment-store.d.ts +2 -2
- package/dist/plugins/tidb-pool.js +11 -23
- package/dist/plugins/workflow-run-store-sql.d.ts +21 -21
- package/dist/plugins/workflow-run-store-sql.js +2 -16
- package/dist/task-cwd.d.ts +12 -1
- package/dist/task-settings.d.ts +6 -0
- package/dist/trace/core-keyset-guard.d.ts +11 -2
- package/dist/trace/project.d.ts +31 -31
- package/dist/trace/project.js +12 -17
- package/dist/trace/redact.js +295 -16
- package/package.json +3 -3
package/dist/trace/project.d.ts
CHANGED
|
@@ -122,8 +122,9 @@ export declare function toolStartEventData(ev: {
|
|
|
122
122
|
/**
|
|
123
123
|
* 🔴 **筛子是一只总函数**(S-239,codex 对抗复审 r1 [medium] 亲核复现后修)—— 上面纪律②许诺的
|
|
124
124
|
* 「坏形 ⇒ report + withhold,**不抛**」此前是靠**逐条枚举**坏形兑现的,枚举不到的那些照样抛:
|
|
125
|
-
* `projectSettlement` 对 `note` / `who.approver` 先 `redactSecrets` 再截断,而 `redactSecrets`
|
|
126
|
-
*
|
|
125
|
+
* `projectSettlement` 对 `note` / `who.approver` 先 `redactSecrets` 再截断,而 `redactSecrets` 会在
|
|
126
|
+
* 病态输入上爆回溯栈(`RangeError`:≥2^23 单段那一形已由 S-219 的 `atLeast` 铸点闭掉,仍能抛的是 JSON 秘密字段臂的
|
|
127
|
+
* 转义对循环,S-102/S-106 登记的既有上界,见 `redact.ts` 该行顶注),两条叶各能抛一次。
|
|
127
128
|
*
|
|
128
129
|
* 抛出去的代价按腿算,而且一条比一条重:写腿落账那处的 `.catch` 看不到**同步**投影期的异常;读腿上它
|
|
129
130
|
* 直接逃出 `formatEvent` / 投影函数 —— 账本裸回放腿的那一次会当场断流(那一行不上帧、游标不前进、后面的
|
|
@@ -135,41 +136,30 @@ export declare function toolStartEventData(ev: {
|
|
|
135
136
|
* `note` 一条叶(`approver` 同病)—— 那是给一个类打两个特判,正是本仓要消掉的形。
|
|
136
137
|
*/
|
|
137
138
|
export declare function screenedGate(v: unknown): GateOutcome | undefined;
|
|
138
|
-
/**
|
|
139
|
-
* 🔴 **耐久行的**门记录读口(7.72.0;`screenedGate` 的读腿孪生)—— 先把本仓自己写过的**旧拼法**读成它
|
|
140
|
-
* 今天的名字({@link RETIRED_ASK_ORIGIN_SPELLINGS}),再走与写腿**同一只**筛子。
|
|
141
|
-
*
|
|
142
|
-
* 为什么读腿要多这一步而写腿不要:读到的字节可能是**本仓上一个版本**写下的(账本回放、park 行的
|
|
143
|
-
* `resolvedOutcome`),而 core 7.14.0 [ref] C2 把 `ASK_ORIGINS` 的一个词改了名 —— 不认旧拼法,那些行就整条
|
|
144
|
-
* withhold,连 `disposition` / `settlement`(谁拒的、人怎么结的)一起在回放面上消失。写腿面对的则是**当下**
|
|
145
|
-
* 引擎铸的记录,它铸不出退役词,所以那里出现退役词 = producer 契约漂了 = 要 report + withhold 的事。
|
|
146
|
-
*
|
|
147
|
-
* 不改原对象(调用方与账本共享引用):归一时换一只信封,原记录一个字节不动。
|
|
148
|
-
*/
|
|
149
|
-
export declare function screenedStoredGate(v: unknown): GateOutcome | undefined;
|
|
150
139
|
/**
|
|
151
140
|
* 🔴 **账本裸回放面的读腿一步**(S-239)—— `GET /v1/runs/:id/events`(`http/routes/runs.ts` 的
|
|
152
|
-
* `formatEvent`)对每一行走这一口,把行上的 `GateOutcome` 交给**同一只** {@link
|
|
141
|
+
* `formatEvent`)对每一行走这一口,把行上的 `GateOutcome` 交给**同一只** {@link screenedGate}。
|
|
153
142
|
*
|
|
154
143
|
* ## 它补的是哪一格
|
|
155
144
|
* 7.72.0 把耐久读边界收成一只筛子,但点名的读腿只有两条({@link toolResultFieldsOf} 的投影回放、
|
|
156
145
|
* `/decide` 幂等回放体)。**账本裸回放**是第三条,而它此前只过 `redactLedgerEventData` —— 那只函数
|
|
157
146
|
* 对 `tool_end` 型**逐字不动**(它管的是 `done.result` / `failed.errorMessage` / park 行三族)。
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
* 透出一个 core 今天判**非成员**的词(消费端按闭集分派 ⇒ 落进它的 default 臂);
|
|
161
|
-
* · 第三方 producer / 坏世代写下的集外词与破不变量的记录 —— 投影面整条 withhold + 计数,裸回放面
|
|
162
|
-
* 直接上 wire,而 `gate-outcome-defective` 那一格不涨(运维看不见有 producer 在发坏记录)。
|
|
147
|
+
* 于是第三方 producer / 坏世代写下的集外词与破不变量的记录在投影面整条 withhold + 计数,裸回放面却
|
|
148
|
+
* 直接上 wire,而 `gate-outcome-defective` 那一格不涨(运维看不见有 producer 在发坏记录)。
|
|
163
149
|
*
|
|
164
150
|
* ## 为什么是这一形(而不是在路由里加一只筛)
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
151
|
+
* 筛子只有一只 —— 本函数只是把**第三条读腿**接到它上面,规则因此是「凡耐久读腿上的 `GateOutcome`
|
|
152
|
+
* 一律过 {@link screenedGate},零例外」。它按**型**分派而不是按调用点,所以将来第二个携带
|
|
153
|
+
* `GateOutcome` 的账本型只需把 {@link GATE_BEARING_LEDGER_EVENT_TYPE} 扩成词表,读腿一行都不用改。
|
|
154
|
+
*
|
|
155
|
+
* ## 读腿与写腿现在是**同一只**筛(7.74.0 起,零双读)
|
|
156
|
+
* 7.72.0–7.73.0 期间读腿多一步「认本服务上一版写下的退役词旧拼法」(`unresolvable` → `ancestor_marked`),
|
|
157
|
+
* 那是一条**长期双读臂**:同一份字节在写腿与读腿上有两种理解,而且没有到期日。7.74.0 把它换成**一次性
|
|
158
|
+
* 迁移**(启动期把存量字节原地改名,`plugins/one-time-migrations.ts`,随 7.75.0 删)⇒ 读腿退回写腿那只
|
|
159
|
+
* 严格筛子,集外词一律整条 withhold + 计数,不再有第二种读法。
|
|
160
|
+
*
|
|
161
|
+
* ## 与写腿脱敏汇点的关系(别再走回头路)
|
|
162
|
+
* 写腿的对偶是 `redactLedgerEventData`(三孪生 store 的 `appendEvent` 落库前),它管的是脱敏、不是判形;
|
|
173
163
|
* 两只函数因此是「共用的脱敏汇点 + 读腿独有的筛」这一层关系,不是同一只函数的两种模式。
|
|
174
164
|
*
|
|
175
165
|
* 不改原对象(调用方与账本共享引用):有变更时换一只信封,原记录一个字节不动。
|
|
@@ -616,7 +606,9 @@ export interface TraceTurn {
|
|
|
616
606
|
* 于是一次 usage 半缺(某些 provider 只回 output 侧)在面板上显示成一个可核对的真数字。
|
|
617
607
|
* 0 是一个数,未知不是 —— 两者同形正是本仓 `boundCallId`/`gateKind`/`contentKind` 一族一直在防的事。
|
|
618
608
|
* ⚠️ wire 面(`GET /v1/tasks/:id/turns` 与 trace SSE 的 `turn` 事件)因此可能给出**只有一侧**的
|
|
619
|
-
* `tokens`;做算术的消费端要按「键可能缺席」读(`tokens` 整只可缺席早就是契约的一部分)。
|
|
609
|
+
* `tokens`;做算术的消费端要按「键可能缺席」读(`tokens` 整只可缺席早就是契约的一部分)。
|
|
610
|
+
* 🔴 **7.74.0 / core 7.17.0([ref])**:引擎自陈 `usageMissing` 的一轮(usage 是六个 0 的占位)⇒ 整只缺席 ——
|
|
611
|
+
* 「未知」在这张脸上只有缺席这一种拼法,不加第二枚座位(见 {@link toContractTokens})。 */
|
|
620
612
|
tokens?: {
|
|
621
613
|
in?: number;
|
|
622
614
|
out?: number;
|
|
@@ -673,8 +665,16 @@ export declare function turnEndEventData(ev: {
|
|
|
673
665
|
* 🔴 S-213⑥:**一条规则管五个子键** —— 源侧在场就铸、缺席就不铸,没有 `?? 0`。此前 `in`/`out` 两座
|
|
674
666
|
* 各带一个默认 0,于是 usage 半缺(provider 只回 output 侧)被投影成 `in:0` = 一个看起来可核对的真
|
|
675
667
|
* 数字;`uncachedIn`/`cacheRead`/`cacheWrite` 三键从第一天起就是缺席制,`in`/`out` 只是漏进了默认值。
|
|
676
|
-
* 早退分支语义不变:输入两键(择一)与 `outputTokens` 全缺 ⇒ 整只 `tokens` 缺席(不铸空对象)。
|
|
677
|
-
|
|
668
|
+
* 早退分支语义不变:输入两键(择一)与 `outputTokens` 全缺 ⇒ 整只 `tokens` 缺席(不铸空对象)。
|
|
669
|
+
*
|
|
670
|
+
* 🔴 **7.74.0 / core 7.17.0([ref]):`usageMissing` 是本投影的输入,不只是事件面的一枚标。**引擎自该版起
|
|
671
|
+
* 在「这一轮 provider 没回 usage 帧」时也**恒发** `turn_end.usage` —— 内容是六个 0 的占位对象,旁边挂
|
|
672
|
+
* `usageMissing: true`。只看 usage 会把「未测量」投成一只全 0 对象,与「真的零用量」在
|
|
673
|
+
* `GET /v1/tasks/:id/turns` 与 trace SSE 的 `turn` 事件上逐字节同形 —— 正是 S-213⑥「0 是一个数,未知不是」
|
|
674
|
+
* 要消灭的那一形。契约里「未知 = `tokens` 整只缺席」早就是唯一读法 ⇒ 标在场就**不铸**(与 7.73.1 及以前的
|
|
675
|
+
* wire 读数逐字节相同),不按「六个 0」的值形猜、也不给 `TraceTurn.tokens` 加第二枚「未知」座(同一事实两种
|
|
676
|
+
* 拼法)。标的域是 core 的 `true | 缺席`(永不 `false`)⇒ 严判 `=== true`,别的值不是标。 */
|
|
677
|
+
export declare function toContractTokens(usage: unknown, usageMissing?: unknown): TraceTurn["tokens"] | undefined;
|
|
678
678
|
/** [ref]C-13:tool_start/tool_end 的**共享挑键**——`projectEvents`(turns 面)与 `mapTraceEvent`
|
|
679
679
|
* (trace SSE 面)对同一账本行的字段选择逐字同构,此前两处独立手写,structured/totalChars/label
|
|
680
680
|
* 三次扩字段各改两遍(见两处对偶注)。收一份:两面各自包装键名皮(id/name/input vs 同名),
|
package/dist/trace/project.js
CHANGED
|
@@ -86,21 +86,21 @@ export function toolStartEventData(ev) {
|
|
|
86
86
|
return { toolCallId: ev.toolCallId, toolName: ev.toolName, ...(ev.label !== undefined ? { label: ev.label } : {}), args: redactDeep(ev.args), ...identityFields(ev) };
|
|
87
87
|
}
|
|
88
88
|
const SETTLEMENT_NOTE_MAX = 500;
|
|
89
|
-
const RETIRED_ASK_ORIGIN_SPELLINGS = {
|
|
90
|
-
unresolvable: "ancestor_marked",
|
|
91
|
-
};
|
|
92
89
|
export function screenedGate(v) {
|
|
93
90
|
try {
|
|
94
91
|
return screenedGateOrThrow(v);
|
|
95
92
|
}
|
|
96
93
|
catch (e) {
|
|
97
|
-
recordFailOpen("server.trace.gate-outcome-defective", `projection threw: ${e instanceof Error ?
|
|
94
|
+
recordFailOpen("server.trace.gate-outcome-defective", `projection threw: ${e instanceof Error ? e.name : "non-Error throw"}`);
|
|
98
95
|
return undefined;
|
|
99
96
|
}
|
|
100
97
|
}
|
|
101
98
|
function screenedGateOrThrow(v) {
|
|
102
|
-
if (v === null || typeof v !== "object")
|
|
99
|
+
if (v === null || typeof v !== "object") {
|
|
100
|
+
if (v !== undefined)
|
|
101
|
+
recordFailOpen("server.trace.gate-outcome-defective", "gate is not an object");
|
|
103
102
|
return undefined;
|
|
103
|
+
}
|
|
104
104
|
const g = v;
|
|
105
105
|
const d = g.disposition;
|
|
106
106
|
if (d === null || typeof d !== "object" || (d.kind !== "allowed" && d.kind !== "denied")) {
|
|
@@ -135,13 +135,6 @@ function screenedGateOrThrow(v) {
|
|
|
135
135
|
...(g.origin === undefined ? {} : { origin: g.origin }),
|
|
136
136
|
};
|
|
137
137
|
}
|
|
138
|
-
export function screenedStoredGate(v) {
|
|
139
|
-
if (v === null || typeof v !== "object")
|
|
140
|
-
return undefined;
|
|
141
|
-
const g = v;
|
|
142
|
-
const renamed = typeof g.origin === "string" ? RETIRED_ASK_ORIGIN_SPELLINGS[g.origin] : undefined;
|
|
143
|
-
return screenedGate(renamed === undefined ? g : { ...g, origin: renamed });
|
|
144
|
-
}
|
|
145
138
|
const GATE_BEARING_LEDGER_EVENT_TYPE = "tool_end";
|
|
146
139
|
export function screenedStoredEventData(type, data) {
|
|
147
140
|
if (type !== GATE_BEARING_LEDGER_EVENT_TYPE)
|
|
@@ -151,7 +144,7 @@ export function screenedStoredEventData(type, data) {
|
|
|
151
144
|
const d = data;
|
|
152
145
|
if (d.gate === undefined)
|
|
153
146
|
return data;
|
|
154
|
-
const gate =
|
|
147
|
+
const gate = screenedGate(d.gate);
|
|
155
148
|
if (gate === undefined) {
|
|
156
149
|
const withheld = { ...d };
|
|
157
150
|
delete withheld.gate;
|
|
@@ -531,7 +524,9 @@ export function turnEndEventData(ev) {
|
|
|
531
524
|
...identityFields(ev),
|
|
532
525
|
};
|
|
533
526
|
}
|
|
534
|
-
export function toContractTokens(usage) {
|
|
527
|
+
export function toContractTokens(usage, usageMissing) {
|
|
528
|
+
if (usageMissing === true)
|
|
529
|
+
return undefined;
|
|
535
530
|
if (!usage || typeof usage !== "object")
|
|
536
531
|
return undefined;
|
|
537
532
|
const u = usage;
|
|
@@ -556,7 +551,7 @@ export function toolCallFieldsOf(d) {
|
|
|
556
551
|
};
|
|
557
552
|
}
|
|
558
553
|
export function toolResultFieldsOf(d) {
|
|
559
|
-
const gate = d.gate === undefined ? undefined :
|
|
554
|
+
const gate = d.gate === undefined ? undefined : screenedGate(d.gate);
|
|
560
555
|
return {
|
|
561
556
|
callId: String(d.toolCallId ?? ""),
|
|
562
557
|
...(typeof d.label === "string" ? { label: d.label } : {}),
|
|
@@ -615,7 +610,7 @@ export function projectEvents(events, opts = {}) {
|
|
|
615
610
|
});
|
|
616
611
|
break;
|
|
617
612
|
case "turn_end":
|
|
618
|
-
flushTurn(ev.seq, ev.ts, toContractTokens(d.usage));
|
|
613
|
+
flushTurn(ev.seq, ev.ts, toContractTokens(d.usage, d.usageMissing));
|
|
619
614
|
break;
|
|
620
615
|
}
|
|
621
616
|
}
|
|
@@ -677,7 +672,7 @@ function mapTraceEventInner(type, seq, data) {
|
|
|
677
672
|
case "tool_end":
|
|
678
673
|
return { event: "tool-result", data: { seq, ...toolResultFieldsOf(data), isError: Boolean(data.isError), ...(data.output !== undefined ? { output: data.output } : {}) } };
|
|
679
674
|
case "turn_end": {
|
|
680
|
-
const tokens = toContractTokens(data.usage);
|
|
675
|
+
const tokens = toContractTokens(data.usage, data.usageMissing);
|
|
681
676
|
return { event: "turn", data: { seq, ...(tokens ? { tokens } : {}) } };
|
|
682
677
|
}
|
|
683
678
|
case "prompt_assembled":
|
package/dist/trace/redact.js
CHANGED
|
@@ -2,14 +2,292 @@ import { 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}»`;
|
|
5
|
+
const atLeast = (cls, min) => `${cls}{${min}}${cls}*`;
|
|
5
6
|
const ENTROPY_SCAN_MAX = 96;
|
|
6
7
|
const twoStrongAhead = (valueClass) => String.raw `(?=${valueClass}{0,${ENTROPY_SCAN_MAX}}[0-9A-Z]${valueClass}{0,${ENTROPY_SCAN_MAX}}[0-9A-Z])`;
|
|
7
8
|
const IDENT_CONNECTORS = "[_./-]";
|
|
8
9
|
const noConnectorAhead = (valueClass) => String.raw `(?!${valueClass}{0,${ENTROPY_SCAN_MAX}}${IDENT_CONNECTORS}${valueClass})`;
|
|
9
|
-
const
|
|
10
|
+
const WORD_INTERIOR_CLASS = "[A-Za-z0-9]";
|
|
11
|
+
const CH_TAB = 9, CH_LF = 10, CH_CR = 13, CH_ESC = 27, CH_SPACE = 32, CH_PLUS = 43, CH_MINUS = 45, CH_SLASH = 47;
|
|
12
|
+
const CH_0 = 48, CH_9 = 57, CH_COLON = 58, CH_EQ = 61, CH_AT = 64, CH_A = 65, CH_Z = 90;
|
|
13
|
+
const CH_LBRACKET = 91, CH_BACKSLASH = 92, CH_UNDERSCORE = 95;
|
|
14
|
+
const CH_a = 97, CH_b = 98, CH_f = 102, CH_n = 110, CH_r = 114, CH_t = 116, CH_u = 117, CH_z = 122, CH_TILDE = 126;
|
|
15
|
+
const CH_QUOTE = 34;
|
|
16
|
+
const CH_CSI8 = 155;
|
|
17
|
+
const CH_LAQUO = 171;
|
|
18
|
+
const ESC_CHAR = String.fromCharCode(CH_ESC);
|
|
19
|
+
const CSI8_CHAR = String.fromCharCode(CH_CSI8);
|
|
20
|
+
const WORD_INTERIOR_BYTES = (() => {
|
|
21
|
+
const one = new RegExp(`^${WORD_INTERIOR_CLASS}$`);
|
|
22
|
+
const table = new Uint8Array(256);
|
|
23
|
+
for (let c = 0; c < table.length; c++)
|
|
24
|
+
if (one.test(String.fromCharCode(c)))
|
|
25
|
+
table[c] = 1;
|
|
26
|
+
return table;
|
|
27
|
+
})();
|
|
28
|
+
function hexDigitValue(c) {
|
|
29
|
+
if (c >= CH_0 && c <= CH_9)
|
|
30
|
+
return c - CH_0;
|
|
31
|
+
if (c >= CH_A && c <= CH_A + 5)
|
|
32
|
+
return c - CH_A + 10;
|
|
33
|
+
if (c >= CH_a && c <= CH_a + 5)
|
|
34
|
+
return c - CH_a + 10;
|
|
35
|
+
return -1;
|
|
36
|
+
}
|
|
37
|
+
function c1LeadLen(s, i, code) {
|
|
38
|
+
if (s.charCodeAt(i) === code)
|
|
39
|
+
return 1;
|
|
40
|
+
if (s.charCodeAt(i) !== CH_BACKSLASH || s.charCodeAt(i + 1) !== CH_u)
|
|
41
|
+
return 0;
|
|
42
|
+
let v = 0;
|
|
43
|
+
for (let k = i + 2; k < i + 6; k++) {
|
|
44
|
+
const d = hexDigitValue(s.charCodeAt(k));
|
|
45
|
+
if (d < 0)
|
|
46
|
+
return 0;
|
|
47
|
+
v = v * 16 + d;
|
|
48
|
+
}
|
|
49
|
+
return v === code ? 6 : 0;
|
|
50
|
+
}
|
|
51
|
+
const CSI_PARAM_RUN = /[0-?]*/y;
|
|
52
|
+
const INTERMEDIATE_RUN = /[ -/]*/y;
|
|
53
|
+
function skipClassRun(re, s, p) {
|
|
54
|
+
if (p >= s.length)
|
|
55
|
+
return p;
|
|
56
|
+
re.lastIndex = p;
|
|
57
|
+
return re.exec(s) === null ? p : re.lastIndex;
|
|
58
|
+
}
|
|
59
|
+
function scanIntermediateChain(s, p, termLo, termHi, mark) {
|
|
60
|
+
const n = s.length;
|
|
61
|
+
for (;;) {
|
|
62
|
+
p = skipClassRun(INTERMEDIATE_RUN, s, p);
|
|
63
|
+
if (p >= n)
|
|
64
|
+
return p;
|
|
65
|
+
const c = s.charCodeAt(p);
|
|
66
|
+
if (c >= termLo && c <= termHi)
|
|
67
|
+
mark(p + 1);
|
|
68
|
+
if (c !== CH_BACKSLASH)
|
|
69
|
+
return p;
|
|
70
|
+
let k = p;
|
|
71
|
+
while (k < n && s.charCodeAt(k) === CH_BACKSLASH)
|
|
72
|
+
k += 1;
|
|
73
|
+
if (k >= n || s.charCodeAt(k) !== CH_QUOTE)
|
|
74
|
+
return p;
|
|
75
|
+
p = k + 1;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const ESCAPED_SEPARATOR_UNITS = [
|
|
79
|
+
{
|
|
80
|
+
leads: ["\\b", "\\f", "\\n", "\\r", "\\t", "\\u"],
|
|
81
|
+
scan: (s, i, mark) => {
|
|
82
|
+
if (s.charCodeAt(i) !== CH_BACKSLASH)
|
|
83
|
+
return i;
|
|
84
|
+
const c = s.charCodeAt(i + 1);
|
|
85
|
+
if (c === CH_b || c === CH_f || c === CH_n || c === CH_r || c === CH_t)
|
|
86
|
+
mark(i + 2);
|
|
87
|
+
else if (c === CH_u && hexDigitValue(s.charCodeAt(i + 2)) >= 0 && hexDigitValue(s.charCodeAt(i + 3)) >= 0
|
|
88
|
+
&& hexDigitValue(s.charCodeAt(i + 4)) >= 0 && hexDigitValue(s.charCodeAt(i + 5)) >= 0)
|
|
89
|
+
mark(i + 6);
|
|
90
|
+
return i;
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
leads: [ESC_CHAR, CSI8_CHAR, "\\u"],
|
|
95
|
+
scan: (s, i, mark) => {
|
|
96
|
+
const esc = c1LeadLen(s, i, CH_ESC);
|
|
97
|
+
let p;
|
|
98
|
+
if (esc > 0) {
|
|
99
|
+
if (s.charCodeAt(i + esc) !== CH_LBRACKET)
|
|
100
|
+
return i;
|
|
101
|
+
p = i + esc + 1;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
const csi8 = c1LeadLen(s, i, CH_CSI8);
|
|
105
|
+
if (csi8 === 0)
|
|
106
|
+
return i;
|
|
107
|
+
p = i + csi8;
|
|
108
|
+
}
|
|
109
|
+
return scanIntermediateChain(s, skipClassRun(CSI_PARAM_RUN, s, p), CH_AT, CH_TILDE, mark);
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
leads: [ESC_CHAR, "\\u"],
|
|
114
|
+
scan: (s, i, mark) => {
|
|
115
|
+
const esc = c1LeadLen(s, i, CH_ESC);
|
|
116
|
+
return esc === 0 ? i : scanIntermediateChain(s, i + esc, CH_0, CH_TILDE, mark);
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
];
|
|
120
|
+
const ESCAPED_SEPARATOR_NEEDLES = [...new Set(ESCAPED_SEPARATOR_UNITS.flatMap((u) => u.leads))];
|
|
121
|
+
const NEEDLE_FIRST_CHARS = [...new Set(ESCAPED_SEPARATOR_NEEDLES.map((n) => n.slice(0, 1)))];
|
|
122
|
+
const RUN_SKIPPABLE_BYTES = (() => {
|
|
123
|
+
const table = new Uint8Array(256);
|
|
124
|
+
for (const first of NEEDLE_FIRST_CHARS) {
|
|
125
|
+
const code = first.charCodeAt(0);
|
|
126
|
+
if (code >= table.length)
|
|
127
|
+
continue;
|
|
128
|
+
const single = ESCAPED_SEPARATOR_NEEDLES.some((n) => n.length === 1 && n.charCodeAt(0) === code);
|
|
129
|
+
const selfPair = ESCAPED_SEPARATOR_NEEDLES.some((n) => n.length > 1 && n.charCodeAt(0) === code && n.charCodeAt(1) === code);
|
|
130
|
+
if (!single && !selfPair)
|
|
131
|
+
table[code] = 1;
|
|
132
|
+
}
|
|
133
|
+
return table;
|
|
134
|
+
})();
|
|
135
|
+
function markEscapedSeparatorTails(s) {
|
|
136
|
+
const any = anyValueArmRe();
|
|
137
|
+
let ends;
|
|
138
|
+
let ascending = true;
|
|
139
|
+
const mark = (end) => {
|
|
140
|
+
if (end > s.length)
|
|
141
|
+
return;
|
|
142
|
+
const tail = s.charCodeAt(end - 1);
|
|
143
|
+
if (tail < WORD_INTERIOR_BYTES.length && WORD_INTERIOR_BYTES[tail] !== 1)
|
|
144
|
+
return;
|
|
145
|
+
any.lastIndex = end;
|
|
146
|
+
if (any.exec(s) === null)
|
|
147
|
+
return;
|
|
148
|
+
if (ends === undefined)
|
|
149
|
+
ends = [end];
|
|
150
|
+
else {
|
|
151
|
+
if (end < ends[ends.length - 1])
|
|
152
|
+
ascending = false;
|
|
153
|
+
ends.push(end);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
const nextAt = new Array(NEEDLE_FIRST_CHARS.length).fill(-1);
|
|
157
|
+
const nextLead = (from) => {
|
|
158
|
+
let best = Number.POSITIVE_INFINITY;
|
|
159
|
+
for (let k = 0; k < NEEDLE_FIRST_CHARS.length; k++) {
|
|
160
|
+
if (nextAt[k] < from) {
|
|
161
|
+
const hit = s.indexOf(NEEDLE_FIRST_CHARS[k], from);
|
|
162
|
+
nextAt[k] = hit === -1 ? Number.POSITIVE_INFINITY : hit;
|
|
163
|
+
}
|
|
164
|
+
if (nextAt[k] < best)
|
|
165
|
+
best = nextAt[k];
|
|
166
|
+
}
|
|
167
|
+
return best === Number.POSITIVE_INFINITY ? -1 : best;
|
|
168
|
+
};
|
|
169
|
+
const n = s.length;
|
|
170
|
+
for (let i = nextLead(0); i >= 0;) {
|
|
171
|
+
let resume = i;
|
|
172
|
+
for (const unit of ESCAPED_SEPARATOR_UNITS) {
|
|
173
|
+
const consumed = unit.scan(s, i, mark);
|
|
174
|
+
if (consumed > resume)
|
|
175
|
+
resume = consumed;
|
|
176
|
+
}
|
|
177
|
+
if (resume === i) {
|
|
178
|
+
const code = s.charCodeAt(i);
|
|
179
|
+
if (code < RUN_SKIPPABLE_BYTES.length && RUN_SKIPPABLE_BYTES[code] === 1) {
|
|
180
|
+
let j = i + 1;
|
|
181
|
+
while (j < n && s.charCodeAt(j) === code)
|
|
182
|
+
j += 1;
|
|
183
|
+
if (j > i + 1)
|
|
184
|
+
resume = j - 1;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
i = nextLead(resume > i ? resume : i + 1);
|
|
188
|
+
}
|
|
189
|
+
if (ends === undefined)
|
|
190
|
+
return undefined;
|
|
191
|
+
if (!ascending)
|
|
192
|
+
ends.sort((x, y) => x - y);
|
|
193
|
+
return ends;
|
|
194
|
+
}
|
|
195
|
+
let escapedTailsText;
|
|
196
|
+
let escapedTailsValue;
|
|
197
|
+
function escapedSeparatorTails(s) {
|
|
198
|
+
if (escapedTailsText !== s) {
|
|
199
|
+
escapedTailsValue = markEscapedSeparatorTails(s);
|
|
200
|
+
escapedTailsText = s;
|
|
201
|
+
}
|
|
202
|
+
return escapedTailsValue;
|
|
203
|
+
}
|
|
204
|
+
function forgetEscapedSeparatorTails() {
|
|
205
|
+
escapedTailsText = undefined;
|
|
206
|
+
escapedTailsValue = undefined;
|
|
207
|
+
}
|
|
208
|
+
const VALUE_ARM_STICKY = new Map();
|
|
209
|
+
const VALUE_ARM_BODIES = [];
|
|
210
|
+
const valueArm = (prefixSrc, valueClass, min) => {
|
|
211
|
+
const body = `${prefixSrc}${twoStrongAhead(valueClass)}${atLeast(valueClass, min)}`;
|
|
212
|
+
const g = new RegExp(`(?<!${WORD_INTERIOR_CLASS})${body}`, "g");
|
|
213
|
+
VALUE_ARM_STICKY.set(g, new RegExp(body, "y"));
|
|
214
|
+
VALUE_ARM_BODIES.push(body);
|
|
215
|
+
return g;
|
|
216
|
+
};
|
|
217
|
+
let anyValueArm;
|
|
218
|
+
function anyValueArmRe() {
|
|
219
|
+
return (anyValueArm ??= new RegExp(`(?:${VALUE_ARM_BODIES.join("|")})`, "y"));
|
|
220
|
+
}
|
|
221
|
+
function runValueArm(g, y, repl, text, wantCount) {
|
|
222
|
+
const tails = escapedSeparatorTails(text);
|
|
223
|
+
if (tails === undefined) {
|
|
224
|
+
if (!wantCount)
|
|
225
|
+
return { out: text.replace(g, repl), count: 0 };
|
|
226
|
+
let hits = 0;
|
|
227
|
+
const replaced = text.replace(g, () => {
|
|
228
|
+
hits += 1;
|
|
229
|
+
return repl;
|
|
230
|
+
});
|
|
231
|
+
return { out: replaced, count: hits };
|
|
232
|
+
}
|
|
233
|
+
let out;
|
|
234
|
+
let copied = 0;
|
|
235
|
+
let count = 0;
|
|
236
|
+
let pos = 0;
|
|
237
|
+
let ti = 0;
|
|
238
|
+
let bIdx = -1;
|
|
239
|
+
let bEnd = -1;
|
|
240
|
+
const nextB = (from) => {
|
|
241
|
+
while (ti < tails.length) {
|
|
242
|
+
const p = tails[ti];
|
|
243
|
+
ti += 1;
|
|
244
|
+
if (p < from)
|
|
245
|
+
continue;
|
|
246
|
+
y.lastIndex = p;
|
|
247
|
+
const m = y.exec(text);
|
|
248
|
+
if (m !== null) {
|
|
249
|
+
bIdx = p;
|
|
250
|
+
bEnd = p + m[0].length;
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
bIdx = -1;
|
|
255
|
+
};
|
|
256
|
+
nextB(0);
|
|
257
|
+
g.lastIndex = 0;
|
|
258
|
+
let a = g.exec(text);
|
|
259
|
+
for (;;) {
|
|
260
|
+
if (a !== null && a.index < pos) {
|
|
261
|
+
g.lastIndex = pos;
|
|
262
|
+
a = g.exec(text);
|
|
263
|
+
}
|
|
264
|
+
if (bIdx >= 0 && bIdx < pos)
|
|
265
|
+
nextB(pos);
|
|
266
|
+
let idx;
|
|
267
|
+
let end;
|
|
268
|
+
if (a !== null && (bIdx < 0 || a.index <= bIdx)) {
|
|
269
|
+
idx = a.index;
|
|
270
|
+
end = a.index + a[0].length;
|
|
271
|
+
}
|
|
272
|
+
else if (bIdx >= 0) {
|
|
273
|
+
idx = bIdx;
|
|
274
|
+
end = bEnd;
|
|
275
|
+
}
|
|
276
|
+
else
|
|
277
|
+
break;
|
|
278
|
+
(out ??= []).push(text.slice(copied, idx), repl);
|
|
279
|
+
copied = end;
|
|
280
|
+
count += 1;
|
|
281
|
+
pos = end;
|
|
282
|
+
}
|
|
283
|
+
if (out === undefined)
|
|
284
|
+
return { out: text, count: 0 };
|
|
285
|
+
out.push(text.slice(copied));
|
|
286
|
+
return { out: out.join(""), count };
|
|
287
|
+
}
|
|
10
288
|
const WORD_SEGMENT_CLASS = "[A-Za-z0-9._~+/-]";
|
|
11
|
-
const keywordArm = (keywordSrc, valueClass, min, tailSrc = "") => new RegExp(`(?:${keywordSrc}\\s+${valueClass
|
|
12
|
-
`|(?<!${WORD_SEGMENT_CLASS})${keywordSrc}(?:${twoStrongAhead(valueClass)}|${noConnectorAhead(valueClass)})${valueClass
|
|
289
|
+
const keywordArm = (keywordSrc, valueClass, min, tailSrc = "") => new RegExp(`(?:${keywordSrc}\\s+${atLeast(valueClass, min)}${tailSrc}` +
|
|
290
|
+
`|(?<!${WORD_SEGMENT_CLASS})${keywordSrc}(?:${twoStrongAhead(valueClass)}|${noConnectorAhead(valueClass)})${atLeast(valueClass, min)}${tailSrc})`, "g");
|
|
13
291
|
const STRUCTURAL_SECRET_PATTERNS = [
|
|
14
292
|
[/-----BEGIN(?:[^-](?:[^-](?:[^-]|-(?!-))*)?)?PRIVATE KEY(?: BLOCK)?-----[\s\S]{0,8192}?-----END(?:[^-](?:[^-](?:[^-]|-(?!-))*)?)?PRIVATE KEY(?: BLOCK)?-----/g, "«redacted:private-key»"],
|
|
15
293
|
[/---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----[\s\S]{0,8192}?---- END SSH2 ENCRYPTED PRIVATE KEY ----/g, "«redacted:private-key»"],
|
|
@@ -26,22 +304,17 @@ const KEYWORD_SECRET_PATTERNS = [
|
|
|
26
304
|
];
|
|
27
305
|
const SHAPE_SECRET_PATTERNS = [
|
|
28
306
|
[valueArm("sk-", "[A-Za-z0-9_-]", 16), "«redacted:key»"],
|
|
29
|
-
[
|
|
30
|
-
[
|
|
31
|
-
[
|
|
32
|
-
[
|
|
33
|
-
[
|
|
34
|
-
[
|
|
35
|
-
[
|
|
307
|
+
[new RegExp(String.raw `(?:ghp|gho|ghu|ghs|ghr)_${atLeast("[A-Za-z0-9]", 20)}`, "g"), "«redacted:gh-token»"],
|
|
308
|
+
[new RegExp(String.raw `github_pat_[A-Za-z0-9]{22}_${atLeast("[A-Za-z0-9]", 59)}`, "g"), "«redacted:gh-token»"],
|
|
309
|
+
[new RegExp(String.raw `xox[baprs]-${atLeast("[A-Za-z0-9-]", 10)}`, "g"), "«redacted:slack-token»"],
|
|
310
|
+
[new RegExp(String.raw `AKIA${atLeast("[0-9A-Z]", 16)}`, "g"), "«redacted:aws-key»"],
|
|
311
|
+
[new RegExp(String.raw `ASIA${atLeast("[0-9A-Z]", 16)}`, "g"), "«redacted:aws-key»"],
|
|
312
|
+
[new RegExp(String.raw `(?:gl(?:pat|dt|rt|rtr|cbt|ptt|oas|ft|imt|agent|wt|soat|ffct)-${atLeast("[A-Za-z0-9_-]", 20)}|(?:sk|rk)_(?:live|test|org)_${atLeast("[A-Za-z0-9]", 24)}|pypi-${atLeast("[A-Za-z0-9_-]", 85)})`, "g"), "«redacted:key»"],
|
|
313
|
+
[new RegExp(String.raw `xapp-\d-${atLeast("[A-Za-z0-9-]", 20)}`, "g"), "«redacted:slack-token»"],
|
|
36
314
|
];
|
|
37
315
|
const FULL_SECRET_PATTERNS = [...STRUCTURAL_SECRET_PATTERNS, ...KEYWORD_SECRET_PATTERNS, ...SHAPE_SECRET_PATTERNS];
|
|
38
316
|
const PEM_HEADER_IDENT_MAX = 31;
|
|
39
317
|
const PEM_BODY_RUN_MIN = 8;
|
|
40
|
-
const CH_TAB = 9, CH_LF = 10, CH_CR = 13, CH_SPACE = 32, CH_PLUS = 43, CH_MINUS = 45, CH_SLASH = 47;
|
|
41
|
-
const CH_0 = 48, CH_9 = 57, CH_COLON = 58, CH_EQ = 61, CH_A = 65, CH_Z = 90, CH_BACKSLASH = 92, CH_UNDERSCORE = 95;
|
|
42
|
-
const CH_a = 97, CH_n = 110, CH_r = 114, CH_t = 116, CH_z = 122;
|
|
43
|
-
const CH_QUOTE = 34;
|
|
44
|
-
const CH_LAQUO = 171;
|
|
45
318
|
const isSepCode = (c) => c === CH_SPACE || c === CH_TAB || c === CH_CR || c === CH_LF;
|
|
46
319
|
const isBase64Code = (c) => (c >= CH_A && c <= CH_Z) || (c >= CH_a && c <= CH_z) || (c >= CH_0 && c <= CH_9) || c === CH_PLUS || c === CH_SLASH || c === CH_EQ;
|
|
47
320
|
const isIdentStartCode = (c) => (c >= CH_A && c <= CH_Z) || (c >= CH_a && c <= CH_z);
|
|
@@ -417,7 +690,7 @@ function redactHeadlessPpkBodies(text) {
|
|
|
417
690
|
const FRAGMENT_SECRET_PATTERNS = [
|
|
418
691
|
[valueArm("sk-", "[A-Za-z0-9_-]", 6), "«redacted:key:partial»"],
|
|
419
692
|
[valueArm("(?:ghp|gho|ghu|ghs|ghr)_", "[A-Za-z0-9]", 8), "«redacted:gh-token:partial»"],
|
|
420
|
-
[
|
|
693
|
+
[new RegExp(String.raw `github_pat_${atLeast("[A-Za-z0-9_]", 8)}`, "g"), "«redacted:gh-token:partial»"],
|
|
421
694
|
[valueArm("xox[baprs]-", "[A-Za-z0-9-]", 4), "«redacted:slack-token:partial»"],
|
|
422
695
|
[valueArm("AKIA", "[0-9A-Z]", 6), "«redacted:aws-key:partial»"],
|
|
423
696
|
[valueArm("ASIA", "[0-9A-Z]", 8), "«redacted:aws-key:partial»"],
|
|
@@ -450,10 +723,15 @@ const regexRule = ([re, repl], arm) => {
|
|
|
450
723
|
throw new Error(`redact: replacement ${JSON.stringify(repl)} uses unsupported ref $${d}`);
|
|
451
724
|
}
|
|
452
725
|
const expand = refs.length === 0 ? undefined : (args) => repl.replace(/\$([1-9])/g, (_, d) => args[Number(d)] ?? "");
|
|
726
|
+
const sticky = VALUE_ARM_STICKY.get(re);
|
|
727
|
+
if (sticky !== undefined && refs.length > 0)
|
|
728
|
+
throw new Error(`redact: value arm replacement ${JSON.stringify(repl)} must not use $N refs`);
|
|
453
729
|
return {
|
|
454
730
|
label: labelOfToken(repl),
|
|
455
731
|
arm,
|
|
456
732
|
run: (text, wantCount, json) => {
|
|
733
|
+
if (sticky !== undefined)
|
|
734
|
+
return runValueArm(re, sticky, repl, text, wantCount);
|
|
457
735
|
const rx = re;
|
|
458
736
|
if (!wantCount)
|
|
459
737
|
return { out: text.replace(rx, repl), count: 0 };
|
|
@@ -700,6 +978,7 @@ export function redactSecrets(text, opts) {
|
|
|
700
978
|
const wantCount = onRedaction !== undefined && (opts?.observe ?? true);
|
|
701
979
|
const json = opts?.json === true;
|
|
702
980
|
const r = applyRules(normalizeForRedaction(text), wantCount, json, opts?.observe ?? true);
|
|
981
|
+
forgetEscapedSeparatorTails();
|
|
703
982
|
reportHits(r.hits);
|
|
704
983
|
return r.out;
|
|
705
984
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.75.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",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"build:binary:run-local:darwin-arm64": "bun build --compile --target=bun-darwin-arm64 src/run-local.ts --outfile dist/run-local-darwin-arm64"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@sema-agent/core": "7.
|
|
57
|
+
"@sema-agent/core": "7.17.2",
|
|
58
58
|
"@sema-agent/settings-schema": "1.10.0",
|
|
59
59
|
"e2b": "^2.28.0",
|
|
60
60
|
"libsodium-wrappers": "^0.8.4",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"sharp": "^0.35.3"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
|
-
"@sema-agent/sdk": "9.
|
|
72
|
+
"@sema-agent/sdk": "9.2.0",
|
|
73
73
|
"@types/libsodium-wrappers": "^0.7.14",
|
|
74
74
|
"@types/node": "22.10.2",
|
|
75
75
|
"@types/pg": "^8.20.0",
|