@sema-agent/server 7.57.0 → 7.58.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/stores.js +12 -5
- package/dist/config-center/http-client.js +7 -11
- package/dist/config-center/read-warnings.d.ts +20 -0
- package/dist/config-center/read-warnings.js +36 -0
- package/dist/config-provider.d.ts +3 -0
- package/dist/config-provider.js +4 -8
- package/dist/hooks/hook-runner.js +8 -8
- package/dist/observability/metrics.js +1 -1
- package/dist/observability/run-terminal-log.js +1 -1
- package/dist/plugins/mailbox-store-sql.d.ts +28 -9
- package/dist/plugins/mailbox-store-sql.js +72 -10
- package/dist/trace/core-keyset-guard.d.ts +5 -6
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/project.d.ts +45 -3
- package/dist/trace/project.js +23 -0
- package/dist/trace/redact.js +7 -3
- package/dist/trace/sema-provenance.d.ts +9 -0
- package/dist/trace/sema-provenance.js +3 -0
- package/package.json +3 -3
package/dist/boot/stores.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
-
import { FileBackgroundAgentStore, FileMailboxStore, FileRosterStore, FileUsageWindowStore, InMemoryUsageWindowStore } from "@sema-agent/core";
|
|
2
|
+
import { FileBackgroundAgentStore, FileMailboxStore, FileRosterStore, FileUsageWindowStore, InMemoryUsageWindowStore, mailboxCrossProcessMountVerdict } from "@sema-agent/core";
|
|
3
3
|
import { createMemorySyncRunner, createMemorySyncTransport } from "../memory-sync-client.js";
|
|
4
4
|
import { memoryEmbedderFor } from "../plugins/memory-embedder.js";
|
|
5
5
|
import { ensurePgMemoryEmbedderMetaSchema, reconcileEmbedderFingerprint } from "../plugins/memory-embedder-fingerprint.js";
|
|
@@ -299,19 +299,26 @@ export async function openStores(ctx) {
|
|
|
299
299
|
const pgPool = backend?.pgPool?.();
|
|
300
300
|
if (pgPool) {
|
|
301
301
|
await ensurePgMailboxSchema(async (text, params) => pgPool.query(text, params));
|
|
302
|
-
mailboxStore = new PgMailboxStore(pgPool);
|
|
302
|
+
mailboxStore = new PgMailboxStore(pgPool, logger);
|
|
303
303
|
}
|
|
304
304
|
else if (mysqlPool) {
|
|
305
305
|
await ensureTiDBMailboxSchema(mysqlPool);
|
|
306
|
-
mailboxStore = new TiDBMailboxStore(mysqlPool);
|
|
306
|
+
mailboxStore = new TiDBMailboxStore(mysqlPool, logger);
|
|
307
307
|
}
|
|
308
308
|
else if (backend?.kind === "local") {
|
|
309
309
|
mailboxStore = new FileMailboxStore(config.localDataRoot ?? localRoot, {
|
|
310
310
|
onCorruptRead: (info) => logger.warn("mailbox_corrupt_read", { path: info.path, reason: info.reason }),
|
|
311
311
|
});
|
|
312
312
|
}
|
|
313
|
-
if (mailboxStore)
|
|
314
|
-
|
|
313
|
+
if (mailboxStore) {
|
|
314
|
+
const laneVerdict = mailboxCrossProcessMountVerdict(mailboxStore);
|
|
315
|
+
logger.info("mailbox_store_enabled", {
|
|
316
|
+
backend: pgPool ? "pg" : mysqlPool ? "tidb" : "file",
|
|
317
|
+
crossProcessSafe: mailboxStore.crossProcessSafe,
|
|
318
|
+
peerLaneMountable: laneVerdict.ok,
|
|
319
|
+
...(laneVerdict.ok ? {} : { peerLaneRefusal: laneVerdict.code }),
|
|
320
|
+
});
|
|
321
|
+
}
|
|
315
322
|
}
|
|
316
323
|
let usageWindowStore;
|
|
317
324
|
if (config.usageWindows) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { readEffectiveWire, PrincipalCapsWire } from "@sema-agent/settings-schema";
|
|
3
3
|
import { centerPromptsFromEffective } from "../prompts-domain-validate.js";
|
|
4
|
+
import { foldEffectiveReadWarning } from "./read-warnings.js";
|
|
4
5
|
export async function fetchEffective(baseUrl, token, etag, fetchImpl = fetch, worker) {
|
|
5
6
|
const scheme = new URL(baseUrl).protocol;
|
|
6
7
|
if (scheme !== "http:" && scheme !== "https:")
|
|
@@ -21,17 +22,12 @@ export async function fetchEffective(baseUrl, token, etag, fetchImpl = fetch, wo
|
|
|
21
22
|
delete wire.prompts;
|
|
22
23
|
else
|
|
23
24
|
wire.prompts = centerPrompts;
|
|
24
|
-
const domainErrors =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
? { domain: w.domain, error: `unknown keys carried to consumer (spelling?): ${w.keys.join(", ")}`.slice(0, 600) }
|
|
31
|
-
: w.kind === "unread-config-file"
|
|
32
|
-
? { domain: w.domain ?? "config.d", error: `unread config file ${w.file} (${w.why})`.slice(0, 600) }
|
|
33
|
-
: undefined)
|
|
34
|
-
.filter((r) => r !== undefined);
|
|
25
|
+
const domainErrors = [];
|
|
26
|
+
for (const w of warnings) {
|
|
27
|
+
const f = foldEffectiveReadWarning(w);
|
|
28
|
+
if (f !== null)
|
|
29
|
+
domainErrors.push(f);
|
|
30
|
+
}
|
|
35
31
|
const effective = wire;
|
|
36
32
|
return { effective, etag: res.headers.get("etag") ?? undefined, ...(domainErrors.length > 0 ? { domainErrors } : {}) };
|
|
37
33
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `EffectiveReadWarning` → 候选门口径的**单点**折叠(本地腿 `config-provider.ts` 与远程腿 `http-client.ts` 共用;
|
|
3
|
+
* 两腿各抄一份映射正是漂移的成因——远程腿曾漏掉一次改动,版本超前的 center 多一个字段就整份远程 refresh 整拒)。
|
|
4
|
+
*
|
|
5
|
+
* **律只有一条:未知键 = 候选门错误,零特例。** 「别的写者按契约放的扩展键」(壳写在 models 条目上的 `sema*`)
|
|
6
|
+
* 不是本函数要分辨的事——那由 settings-schema 的 `EXTENSION_NAMESPACES` 在**声明处**说清(登记的键既不点名也不计数,
|
|
7
|
+
* 1.5.0 起);到了这里的每一个键都是「写了没生效」的拼错或越界,与 domain-defaulted 同级。此前本仓在下游按 kind/
|
|
8
|
+
* 按域/按键路径闭集/按 64 满额四层猜意图,每层都被下一轮复审打穿——意图只能在声明处说,消费方不猜。
|
|
9
|
+
*
|
|
10
|
+
* 截断:schema 的键列表有界(64),超界带 `truncatedFrom`(真实总数)——文案如实带上,消费方永远不把有界列表当穷举。
|
|
11
|
+
* 词表是闭集:`switch` 穷举 `kind`,schema 加新 kind 这里编译红,不会静默漏臂。
|
|
12
|
+
*/
|
|
13
|
+
import type { EffectiveReadWarning } from "@sema-agent/settings-schema";
|
|
14
|
+
export type FoldedReadWarning = {
|
|
15
|
+
readonly domain: string;
|
|
16
|
+
readonly error: string;
|
|
17
|
+
};
|
|
18
|
+
/** null = 不入诊断面(`hosts-grandfathered`:值已被收编接受,与本地店同口径)。 */
|
|
19
|
+
export declare function foldEffectiveReadWarning(w: EffectiveReadWarning): FoldedReadWarning | null;
|
|
20
|
+
//# sourceMappingURL=read-warnings.d.ts.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const MAX_ERROR_CHARS = 600;
|
|
2
|
+
const cap = (s) => s.slice(0, MAX_ERROR_CHARS);
|
|
3
|
+
const keyList = (prefix, keys, truncatedFrom) => {
|
|
4
|
+
const total = truncatedFrom ?? keys.length;
|
|
5
|
+
const budget = MAX_ERROR_CHARS - prefix.length;
|
|
6
|
+
const shown = [];
|
|
7
|
+
let used = 0;
|
|
8
|
+
const suffixFor = (n) => (n < total ? ` (+${total - n} more, list truncated)` : "");
|
|
9
|
+
for (const k of keys) {
|
|
10
|
+
const piece = (shown.length > 0 ? ", " : "") + k;
|
|
11
|
+
if (used + piece.length + suffixFor(shown.length + 1).length > budget)
|
|
12
|
+
break;
|
|
13
|
+
shown.push(k);
|
|
14
|
+
used += piece.length;
|
|
15
|
+
}
|
|
16
|
+
return `${prefix}${shown.join(", ")}${suffixFor(shown.length)}`;
|
|
17
|
+
};
|
|
18
|
+
export function foldEffectiveReadWarning(w) {
|
|
19
|
+
switch (w.kind) {
|
|
20
|
+
case "hosts-grandfathered":
|
|
21
|
+
return null;
|
|
22
|
+
case "domain-defaulted":
|
|
23
|
+
return { domain: w.domain, error: cap(w.error instanceof Error ? w.error.message : String(w.error)) };
|
|
24
|
+
case "unknown-keys-dropped":
|
|
25
|
+
return { domain: w.domain, error: keyList("unknown keys dropped by domain schema: ", w.keys, w.truncatedFrom) };
|
|
26
|
+
case "unknown-keys-carried":
|
|
27
|
+
return { domain: w.domain, error: keyList("unknown keys carried to consumer (spelling?): ", w.keys, w.truncatedFrom) };
|
|
28
|
+
case "unread-config-file":
|
|
29
|
+
return { domain: w.domain ?? "config.d", error: cap(`unread config file ${w.file} (${w.why})`) };
|
|
30
|
+
default: {
|
|
31
|
+
const never = w;
|
|
32
|
+
throw new Error(`unhandled EffectiveReadWarning kind: ${JSON.stringify(never)}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=read-warnings.js.map
|
|
@@ -7,6 +7,9 @@ import { fetchEffective as remoteFetchEffective, fetchSkillContent as remoteFetc
|
|
|
7
7
|
* 连坐全包回落 env——坏域按该域 schema default 落+错误单列,好域照常生效;caller(main.ts)对每条打
|
|
8
8
|
* per-domain warn 点名坏域。gate 域(governance/rosters/…)坏文件 tolerant 仍 throw(READ 容错不放宽
|
|
9
9
|
* gate fail-loud,registry-core 半场精化)——那条路走既有的整包 catch。 */
|
|
10
|
+
/** 壳写在 `config.d/models.json` 条目上的装饰键(`sema*`)自 settings-schema 1.5.0 起是**声明过的扩展命名空间**
|
|
11
|
+
* (`EXTENSION_NAMESPACES`):schema 照旧 strip、但不再报「未知键」,所以本函数收到的每一个未知键都是拼错/越界 ⇒ 候选门错误。
|
|
12
|
+
* 此前把这类键当「通知」放行的整套下游机制(域豁免/键路径闭集/满额特判/单独的通知面)已全部撤回——意图在声明处说,消费方不猜。 */
|
|
10
13
|
export type FetchEffectiveResult = {
|
|
11
14
|
effective: EffectiveConfig;
|
|
12
15
|
etag?: string;
|
package/dist/config-provider.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHmac, randomBytes } from "node:crypto";
|
|
2
|
+
import { foldEffectiveReadWarning } from "./config-center/read-warnings.js";
|
|
2
3
|
import { FileConfigStore } from "@sema-agent/settings-schema/node";
|
|
3
4
|
import { findSkillContent, skillContentHash, refIntegrityIssues, siblingResolver, } from "@sema-agent/settings-schema";
|
|
4
5
|
import { redactSecrets } from "./trace/redact.js";
|
|
@@ -62,14 +63,9 @@ export class LocalConfigProvider {
|
|
|
62
63
|
const store = this.#injectedStore ??
|
|
63
64
|
new FileConfigStore(this.root, {
|
|
64
65
|
onWarning: (w) => {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
parseWarnings.push({ domain: w.domain, error: `unknown keys dropped by domain schema: ${w.keys.join(", ")}` });
|
|
69
|
-
else if (w.kind === "unknown-keys-carried")
|
|
70
|
-
parseWarnings.push({ domain: w.domain, error: `unknown keys carried to consumer (spelling?): ${w.keys.join(", ")}` });
|
|
71
|
-
else if (w.kind === "unread-config-file")
|
|
72
|
-
parseWarnings.push({ domain: w.domain ?? "config.d", error: `unread config file ${w.file} (${w.why})` });
|
|
66
|
+
const f = foldEffectiveReadWarning(w);
|
|
67
|
+
if (f !== null)
|
|
68
|
+
parseWarnings.push(f);
|
|
73
69
|
},
|
|
74
70
|
});
|
|
75
71
|
const { effective: eff, domainErrors } = await store.getEffective({ tolerant: true });
|
|
@@ -673,7 +673,7 @@ async function runObserveOnlyEvent(event, groups, matchValue, payload, ctx, once
|
|
|
673
673
|
if (!out)
|
|
674
674
|
continue;
|
|
675
675
|
if (typeof out.systemMessage === "string")
|
|
676
|
-
ctx.logger.
|
|
676
|
+
ctx.logger.info("hook_system_message", { event, message: clip(out.systemMessage, 500) });
|
|
677
677
|
if (out.decision !== undefined || out.continue === false || out.hookSpecificOutput !== undefined) {
|
|
678
678
|
ctx.logger.warn("hook_result_unsupported", { event });
|
|
679
679
|
}
|
|
@@ -747,7 +747,7 @@ export function createTaskHooks(config, ctx) {
|
|
|
747
747
|
if (!out)
|
|
748
748
|
continue;
|
|
749
749
|
if (typeof out.systemMessage === "string")
|
|
750
|
-
ctx.logger.
|
|
750
|
+
ctx.logger.info("hook_system_message", { event: "PreToolUse", message: clip(out.systemMessage, 500) });
|
|
751
751
|
if (out.continue === false) {
|
|
752
752
|
const reason = clip(typeof out.stopReason === "string" ? out.stopReason : "hook requested stop", MAX_HOOK_FEEDBACK_CHARS);
|
|
753
753
|
return { action: "deny", message: reason, ...buildHookContextField(contexts) };
|
|
@@ -845,7 +845,7 @@ export function createTaskHooks(config, ctx) {
|
|
|
845
845
|
if (!out)
|
|
846
846
|
continue;
|
|
847
847
|
if (typeof out.systemMessage === "string")
|
|
848
|
-
ctx.logger.
|
|
848
|
+
ctx.logger.info("hook_system_message", { event: "PostToolUse", message: clip(out.systemMessage, 500) });
|
|
849
849
|
if (out.continue === false) {
|
|
850
850
|
ctx.logger.warn("hook_continue_false_unsupported", { event: "PostToolUse" });
|
|
851
851
|
}
|
|
@@ -895,7 +895,7 @@ export function createTaskHooks(config, ctx) {
|
|
|
895
895
|
if (!out)
|
|
896
896
|
continue;
|
|
897
897
|
if (typeof out.systemMessage === "string")
|
|
898
|
-
ctx.logger.
|
|
898
|
+
ctx.logger.info("hook_system_message", { event: "PostToolUseFailure", message: clip(out.systemMessage, 500) });
|
|
899
899
|
if (out.continue === false)
|
|
900
900
|
ctx.logger.warn("hook_continue_false_unsupported", { event: "PostToolUseFailure" });
|
|
901
901
|
if (out.decision === "block" && typeof out.reason === "string")
|
|
@@ -942,7 +942,7 @@ export function createTaskHooks(config, ctx) {
|
|
|
942
942
|
if (!out)
|
|
943
943
|
continue;
|
|
944
944
|
if (typeof out.systemMessage === "string")
|
|
945
|
-
ctx.logger.
|
|
945
|
+
ctx.logger.info("hook_system_message", { event: "PostToolBatch", message: clip(out.systemMessage, 500) });
|
|
946
946
|
if (out.continue === false)
|
|
947
947
|
ctx.logger.warn("hook_continue_false_unsupported", { event: "PostToolBatch" });
|
|
948
948
|
if (out.decision === "block" && typeof out.reason === "string")
|
|
@@ -974,7 +974,7 @@ export function createTaskHooks(config, ctx) {
|
|
|
974
974
|
if (!out)
|
|
975
975
|
continue;
|
|
976
976
|
if (typeof out.systemMessage === "string")
|
|
977
|
-
ctx.logger.
|
|
977
|
+
ctx.logger.info("hook_system_message", { event: "UserPromptSubmit", message: clip(out.systemMessage, 500) });
|
|
978
978
|
if (out.continue === false) {
|
|
979
979
|
const reason = clip(typeof out.stopReason === "string" ? out.stopReason : "hook requested stop", MAX_HOOK_FEEDBACK_CHARS);
|
|
980
980
|
return { block: reason, ...buildHookContextField(contexts) };
|
|
@@ -1063,7 +1063,7 @@ export function createTaskHooks(config, ctx) {
|
|
|
1063
1063
|
continue;
|
|
1064
1064
|
}
|
|
1065
1065
|
if (typeof out.systemMessage === "string")
|
|
1066
|
-
ctx.logger.
|
|
1066
|
+
ctx.logger.info("hook_system_message", { event: "Stop", message: clip(out.systemMessage, 500) });
|
|
1067
1067
|
if (out.continue === false)
|
|
1068
1068
|
sawContinueFalse = true;
|
|
1069
1069
|
if (out.decision === "block" && block === undefined) {
|
|
@@ -1122,7 +1122,7 @@ export function createTaskHooks(config, ctx) {
|
|
|
1122
1122
|
if (!out)
|
|
1123
1123
|
continue;
|
|
1124
1124
|
if (typeof out.systemMessage === "string")
|
|
1125
|
-
ctx.logger.
|
|
1125
|
+
ctx.logger.info("hook_system_message", { event: "PreCompact", message: clip(out.systemMessage, 500) });
|
|
1126
1126
|
if (out.continue === false) {
|
|
1127
1127
|
ctx.logger.warn("hook_continue_false_unsupported", { event: "PreCompact" });
|
|
1128
1128
|
}
|
|
@@ -235,7 +235,7 @@ export function createMetrics() {
|
|
|
235
235
|
m.counter("remote_env_resume_total", "E2B remote-env resumeVM outcomes (v1.5, provider/result)");
|
|
236
236
|
m.counter("remote_env_exec_stream_read_timeout_total", "E2B execStream idle read-timeouts (#1128, provider)");
|
|
237
237
|
m.counter("resume_anchor_capture_failed", "E18 resume-at per-turn anchor captures that failed (best-effort, run unaffected)");
|
|
238
|
-
m.counter("brain_retry_total", "Brain-layer status events (S4), by phase (rate_limited/retrying/reconnecting/circuit_open)");
|
|
238
|
+
m.counter("brain_retry_total", "Brain-layer status events (S4), by phase (rate_limited/retrying/reconnecting/circuit_open/waiting_first_token/recovered/gave_up)");
|
|
239
239
|
m.gauge("store_backend_degraded", "1 when the auto DB probe failed at boot and this replica degraded to in-memory (S5)");
|
|
240
240
|
m.counter("images_omitted_total", "Tasks whose images will be degraded to text placeholders (no-vision model), by model (S6)");
|
|
241
241
|
m.counter("runs_reaped_total", "Run rows flipped by the background reapers (S7), by kind (stale/suspended/expired_checkpoint)");
|
|
@@ -177,7 +177,7 @@ export const OVERSIZED_PATCH_PLACEHOLDER = "«redacted:oversized-patch-not-inlin
|
|
|
177
177
|
function diffScanRefusal(patch, direct) {
|
|
178
178
|
if (/^diff --(?:cc|combined) |^@{3,} /m.test(patch))
|
|
179
179
|
return "combined-diff";
|
|
180
|
-
const fence = /-{4,5} ?BEGIN[^\n-]*PRIVATE KEY(?: BLOCK)?[^\n-]*-{4,5}/;
|
|
180
|
+
const fence = /-{4,5} ?BEGIN(?:[^\n-]|-(?!-))*PRIVATE KEY(?: BLOCK)?(?:[^\n-]|-(?!-))*-{4,5}/;
|
|
181
181
|
if (fence.test(direct))
|
|
182
182
|
return "pem-fence-survived";
|
|
183
183
|
const ppkField = /Private-(?:MAC|Hash): [0-9a-fA-F]{32}/;
|
|
@@ -7,15 +7,19 @@
|
|
|
7
7
|
* 终版文本,conformance 无两可):
|
|
8
8
|
* 1. **同主重 claim = 续期 + 纳新**(crash-retry 正形):活 lease 只挡**别的** owner;同 owner 任意
|
|
9
9
|
* 时刻重 claim 重设 TTL 并拿到当前全部未 ack 消息(maxSeq 随最新行走)。
|
|
10
|
-
* 2. **seq = 盒生命周期内不复用**:box 行携 `next_seq`,分配与 append 同事务;`drop
|
|
11
|
-
*
|
|
10
|
+
* 2. **seq = 盒生命周期内不复用**:box 行携 `next_seq`,分配与 append 同事务;`drop` 删整盒(含 box 行)
|
|
11
|
+
* = 生命周期终结,重建从 1(core 定谳:行随盒亡,dedup 键携 handle)。⚠️ **`reap` 自 [ref](core
|
|
12
|
+
* 2026-07-25)起不再与 drop 同句**:reap 是收件人活着时的龄扫,**清箱保高水位**(删消息、清租约、盒行与
|
|
13
|
+
* next_seq 留着)——本文件 2026-09-03(车BF,codex r1 [high])才跟上;此前 reap 删盒行 ⇒ 下一条 append 从 1
|
|
14
|
+
* 重铸,陈旧 ack 可删掉一条没人见过的新消息。跨进程试剂盒明钉此条。
|
|
12
15
|
* 3. **ack 无 owner 守卫**(信任 claim 赢者链):`seq <= upToSeq` 删行;lease 的 `maxSeq <= upToSeq`
|
|
13
16
|
* 时顺带清 lease(全量 ack = 释放)。
|
|
14
17
|
* 4. **lease 过期判 = 严格 `expiresAt > now` 才挡**;`releaseLease` 只在 owner 匹配时清。
|
|
15
18
|
* 5. `peekCount` 含已 lease 消息(可见性 ≠ lease 态);空盒 claim = null(box 行在而消息 0 也 null)。
|
|
16
19
|
* 6. **reap = SQL 全量真扫**(core File 实现只扫已加载盒 = 进程内 backstop,[ref] 确认 SQL 是全量
|
|
17
|
-
* 真扫的正确层):`maxAgeMs` 缺省 → 0;按盒 newest `sentAt < now - maxAgeMs`
|
|
18
|
-
* box 行无消息)
|
|
20
|
+
* 真扫的正确层):`maxAgeMs` 缺省 → 0;按盒 newest `sentAt < now - maxAgeMs` **清箱**(删消息 + 清租约,
|
|
21
|
+
* 盒行/高水位保留,见定谳 2 的 [ref] 半句);空盒(有 box 行无消息)不清(InMemory 同形——`newest ===
|
|
22
|
+
* undefined` 不动)。
|
|
19
23
|
*
|
|
20
24
|
* 键列 = 字节等价(TiDB VARBINARY / PG COLLATE "C",roster F2 案):scope/handle/lease_owner 的
|
|
21
25
|
* `=` 必须纯字节——PAD SPACE 近撞不得穿隔离/抢别人 lease。PG unstorable bytes([ref] 三层定谳):
|
|
@@ -37,15 +41,28 @@
|
|
|
37
41
|
*/
|
|
38
42
|
import type { Pool as MySqlPool } from "mysql2/promise";
|
|
39
43
|
import type { Pool as PgPool } from "pg";
|
|
40
|
-
import type { MailboxLease, MailboxStore } from "@sema-agent/core";
|
|
41
|
-
type MailboxAppendMessage = Parameters<MailboxStore["append"]>[2];
|
|
44
|
+
import type { MailboxAppendMessage, MailboxLease, MailboxStore } from "@sema-agent/core";
|
|
42
45
|
export declare const MAILBOX_TABLE = "mailbox";
|
|
43
46
|
export declare const MAILBOX_MSG_TABLE = "mailbox_message";
|
|
47
|
+
/** 店侧日志座(可选;boot 注入 `Logger`)。7.58.0 合并重扫 [medium]:坏 `peer_meta` 行的 fail-closed 抛必须**响亮**——core 唯一的
|
|
48
|
+
* claimLease 调用方(peer-session-drain)吞掉该错,店不记这一笔就成了「静默永久卡箱」。抛之前记 `mailbox_peer_meta_corrupt`
|
|
49
|
+
* (scope/handle/seq/code),运维按 seq 修行。 */
|
|
50
|
+
export interface MailboxStoreLogger {
|
|
51
|
+
warn(event: string, fields: Record<string, unknown>): void;
|
|
52
|
+
}
|
|
44
53
|
export declare function ensureTiDBMailboxSchema(pool: MySqlPool): Promise<void>;
|
|
45
54
|
export declare function ensurePgMailboxSchema(q: (text: string, params?: unknown[]) => Promise<unknown>): Promise<void>;
|
|
46
55
|
export declare class TiDBMailboxStore implements MailboxStore {
|
|
47
56
|
private readonly pool;
|
|
48
|
-
|
|
57
|
+
private readonly logger?;
|
|
58
|
+
/** [ref]([ref] §1.2⑥):跨进程安全**显式**声明 —— core 的 `mailboxCrossProcessMountVerdict` 只对字面 `true` 放行
|
|
59
|
+
* peer lane(缺席/false/畸形一律响亮拒挂 `config.peer_lane_unmounted`)。真值前提 = 本文件头注「并发正确性」段:seq
|
|
60
|
+
* 在事务内经盒行 FOR UPDATE 单铸、lease/ack/drop/reap 统一盒行锁序、reap 事务内逐盒当前读重验 —— 多副本(=多 OS
|
|
61
|
+
* 进程)共享一个盒本就是 SQL 双生的设计象限。**终验** = core 跨进程试剂盒 `mailboxCrossProcessContract` 跑在真第二个
|
|
62
|
+
* OS 进程上:`test/mailbox-cross-process-kit.test.ts`(file 腿恒跑验 harness;tidb/pg 腿 ENV-GATED,发车前双库门必跑,
|
|
63
|
+
* 红即摘本声明——core 契约逐字「declares true AFTER passing」,声明不许先于试剂盒长期悬空)。 */
|
|
64
|
+
readonly crossProcessSafe = true;
|
|
65
|
+
constructor(pool: MySqlPool, logger?: MailboxStoreLogger | undefined);
|
|
49
66
|
private tx;
|
|
50
67
|
append(scope: string, handle: string, msg: MailboxAppendMessage): Promise<number>;
|
|
51
68
|
claimLease(scope: string, handle: string, owner: string, ttlMs: number, now?: number): Promise<MailboxLease | null>;
|
|
@@ -59,7 +76,10 @@ export declare class TiDBMailboxStore implements MailboxStore {
|
|
|
59
76
|
}
|
|
60
77
|
export declare class PgMailboxStore implements MailboxStore {
|
|
61
78
|
private readonly pool;
|
|
62
|
-
|
|
79
|
+
private readonly logger?;
|
|
80
|
+
/** [ref] 跨进程安全显式声明(TiDB 孪生同注:事务 + 盒行 FOR UPDATE 锁序是真值前提;终验 = test/mailbox-cross-process-kit.test.ts pg 腿)。 */
|
|
81
|
+
readonly crossProcessSafe = true;
|
|
82
|
+
constructor(pool: PgPool, logger?: MailboxStoreLogger | undefined);
|
|
63
83
|
private tx;
|
|
64
84
|
/** 身份键拒绝式([ref] 协议纪律):scope/handle/owner/from 清洗形变 = 路由/attribution 错位。 */
|
|
65
85
|
private assertIdentity;
|
|
@@ -73,5 +93,4 @@ export declare class PgMailboxStore implements MailboxStore {
|
|
|
73
93
|
maxAgeMs?: number;
|
|
74
94
|
}): Promise<number>;
|
|
75
95
|
}
|
|
76
|
-
export {};
|
|
77
96
|
//# sourceMappingURL=mailbox-store-sql.d.ts.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MAILBOX_INVALID_PEER_META_CODE, MailboxStoreError, readMailboxPeerMeta } from "@sema-agent/core";
|
|
1
2
|
import { pgHasUnstorable, pgSanitizeText, PgUnstorableError } from "./pg-safe-json.js";
|
|
2
3
|
export const MAILBOX_TABLE = "mailbox";
|
|
3
4
|
export const MAILBOX_MSG_TABLE = "mailbox_message";
|
|
@@ -43,14 +44,61 @@ function decodeHopChain(raw) {
|
|
|
43
44
|
return undefined;
|
|
44
45
|
return parsed;
|
|
45
46
|
}
|
|
47
|
+
const PEER_META_MAX_BYTES = 60_000;
|
|
48
|
+
function encodePeerMeta(raw) {
|
|
49
|
+
const meta = readMailboxPeerMeta(raw);
|
|
50
|
+
if (meta === undefined)
|
|
51
|
+
return null;
|
|
52
|
+
const text = JSON.stringify(meta);
|
|
53
|
+
if (Buffer.byteLength(text, "utf8") > PEER_META_MAX_BYTES) {
|
|
54
|
+
throw new Error(`MailboxStore: peerMeta exceeds the ${PEER_META_MAX_BYTES}-byte column (refusing — a truncated record would silently read back as ABSENT, which the drain treats as a foreign record)`);
|
|
55
|
+
}
|
|
56
|
+
return text;
|
|
57
|
+
}
|
|
58
|
+
function decodePeerMeta(raw) {
|
|
59
|
+
if (raw === null || raw === undefined)
|
|
60
|
+
return undefined;
|
|
61
|
+
const text = bufToStr(raw);
|
|
62
|
+
let parsed;
|
|
63
|
+
try {
|
|
64
|
+
parsed = JSON.parse(text);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
throw new MailboxStoreError(MAILBOX_INVALID_PEER_META_CODE, `MailboxStore: stored peer_meta is not JSON (refusing the lease — a corrupt typed record must not be read as "no metadata"): ${err instanceof Error ? err.message : String(err)}`);
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
return readMailboxPeerMeta(parsed);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
if (err instanceof MailboxStoreError)
|
|
74
|
+
throw err;
|
|
75
|
+
throw new MailboxStoreError(MAILBOX_INVALID_PEER_META_CODE, `MailboxStore: stored peer_meta failed validation (refusing the lease): ${err instanceof Error ? err.message : String(err)}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function rowsToMessages(rows, scope, handle, logger) {
|
|
79
|
+
const out = [];
|
|
80
|
+
for (const r of rows) {
|
|
81
|
+
try {
|
|
82
|
+
out.push(rowToMessage(r));
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
if (err instanceof MailboxStoreError)
|
|
86
|
+
logger?.warn("mailbox_peer_meta_corrupt", { scope, handle, seq: Number(r.seq), code: err.code, note: "claimLease refused for the whole box (fail-closed); repair or NULL the peer_meta column of this row" });
|
|
87
|
+
throw err;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
46
92
|
function rowToMessage(r) {
|
|
47
93
|
const hopChain = decodeHopChain(r.hop_chain);
|
|
94
|
+
const peerMeta = decodePeerMeta(r.peer_meta);
|
|
48
95
|
return {
|
|
49
96
|
seq: Number(r.seq),
|
|
50
97
|
...(r.from_id !== null && r.from_id !== undefined ? { from: bufToStr(r.from_id) } : {}),
|
|
51
98
|
content: String(r.content),
|
|
52
99
|
sentAt: Number(r.sent_at_ms),
|
|
53
100
|
...(hopChain !== undefined ? { hopChain } : {}),
|
|
101
|
+
...(peerMeta !== undefined ? { peerMeta } : {}),
|
|
54
102
|
};
|
|
55
103
|
}
|
|
56
104
|
export async function ensureTiDBMailboxSchema(pool) {
|
|
@@ -75,6 +123,11 @@ export async function ensureTiDBMailboxSchema(pool) {
|
|
|
75
123
|
-- 见 encodeHopChain 头注:缺席 vs 空数组绝不互折)。TEXT 而非 LONGTEXT——链上界在 core(28 跳,
|
|
76
124
|
-- 短 token),写入面另有 HOP_CHAIN_MAX_BYTES fail-loud 守卫关掉静默截断类。
|
|
77
125
|
hop_chain TEXT NULL,
|
|
126
|
+
-- peer_meta(S-90;core fc52b4d4 design/385 片2b):drain 点权威判断读的 typed peer 记录,JSON 文本;
|
|
127
|
+
-- NULL = 缺席(**有语义**:pre-385/foreign 记录,drain 永不回溯准入;与空对象绝不互折)。写入面先过 core
|
|
128
|
+
-- readMailboxPeerMeta(坏值带码拒于任何副作用前)+ PEER_META_MAX_BYTES 守卫关掉静默截断类。
|
|
129
|
+
-- 新列按 2026-07-26 schema policy 直接折进 CREATE(不开 ALTER seam,存量库删库重建)。
|
|
130
|
+
peer_meta TEXT NULL,
|
|
78
131
|
PRIMARY KEY (scope_key, handle, seq),
|
|
79
132
|
KEY idx_mbm_sent (scope_key, handle, sent_at_ms)
|
|
80
133
|
) COLLATE utf8mb4_bin`);
|
|
@@ -98,14 +151,18 @@ export async function ensurePgMailboxSchema(q) {
|
|
|
98
151
|
content TEXT COLLATE "C" NOT NULL,
|
|
99
152
|
sent_at_ms BIGINT NOT NULL,
|
|
100
153
|
hop_chain TEXT COLLATE "C", -- core 5.17.0 design/176(TiDB twin 同注:NULL = 缺席且有语义)
|
|
154
|
+
peer_meta TEXT COLLATE "C", -- S-90 core fc52b4d4 design/385 片2b(TiDB twin 同注:typed peer 记录 JSON;NULL = 缺席且有语义)
|
|
101
155
|
PRIMARY KEY (scope_key, handle, seq)
|
|
102
156
|
)`);
|
|
103
157
|
await q(`CREATE INDEX IF NOT EXISTS idx_mbm_sent ON ${MAILBOX_MSG_TABLE} (scope_key, handle, sent_at_ms)`);
|
|
104
158
|
}
|
|
105
159
|
export class TiDBMailboxStore {
|
|
106
160
|
pool;
|
|
107
|
-
|
|
161
|
+
logger;
|
|
162
|
+
crossProcessSafe = true;
|
|
163
|
+
constructor(pool, logger) {
|
|
108
164
|
this.pool = pool;
|
|
165
|
+
this.logger = logger;
|
|
109
166
|
}
|
|
110
167
|
async tx(fn) {
|
|
111
168
|
const c = await this.pool.getConnection();
|
|
@@ -130,6 +187,7 @@ export class TiDBMailboxStore {
|
|
|
130
187
|
assertKeyBytes("scope", scope);
|
|
131
188
|
assertKeyBytes("handle", handle);
|
|
132
189
|
const hopChain = encodeHopChain(msg.hopChain);
|
|
190
|
+
const peerMeta = encodePeerMeta(msg.peerMeta);
|
|
133
191
|
return this.tx(async (c) => {
|
|
134
192
|
await c.query(`INSERT IGNORE INTO ${MAILBOX_TABLE} (scope_key, handle, scope, next_seq) VALUES (?, ?, ?, 1)`, [scope, handle, scope]);
|
|
135
193
|
let [rows] = (await c.query(`SELECT next_seq FROM ${MAILBOX_TABLE} WHERE scope_key = ? AND handle = ? FOR UPDATE`, [scope, handle]));
|
|
@@ -139,7 +197,7 @@ export class TiDBMailboxStore {
|
|
|
139
197
|
}
|
|
140
198
|
const seq = Number(rows[0].next_seq);
|
|
141
199
|
await c.query(`UPDATE ${MAILBOX_TABLE} SET next_seq = ? WHERE scope_key = ? AND handle = ?`, [seq + 1, scope, handle]);
|
|
142
|
-
await c.query(`INSERT INTO ${MAILBOX_MSG_TABLE} (scope_key, handle, seq, from_id, content, sent_at_ms, hop_chain) VALUES (?, ?, ?, ?, ?, ?, ?)`, [scope, handle, seq, msg.from ?? null, msg.content, msg.sentAt, hopChain]);
|
|
200
|
+
await c.query(`INSERT INTO ${MAILBOX_MSG_TABLE} (scope_key, handle, seq, from_id, content, sent_at_ms, hop_chain, peer_meta) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [scope, handle, seq, msg.from ?? null, msg.content, msg.sentAt, hopChain, peerMeta]);
|
|
143
201
|
return seq;
|
|
144
202
|
});
|
|
145
203
|
}
|
|
@@ -154,12 +212,12 @@ export class TiDBMailboxStore {
|
|
|
154
212
|
bufToStr(box.lease_owner) !== owner) {
|
|
155
213
|
return null;
|
|
156
214
|
}
|
|
157
|
-
const [msgs] = (await c.query(`SELECT seq, from_id, content, sent_at_ms, hop_chain FROM ${MAILBOX_MSG_TABLE} WHERE scope_key = ? AND handle = ? ORDER BY seq FOR UPDATE`, [scope, handle]));
|
|
215
|
+
const [msgs] = (await c.query(`SELECT seq, from_id, content, sent_at_ms, hop_chain, peer_meta FROM ${MAILBOX_MSG_TABLE} WHERE scope_key = ? AND handle = ? ORDER BY seq FOR UPDATE`, [scope, handle]));
|
|
158
216
|
if (msgs.length === 0)
|
|
159
217
|
return null;
|
|
160
218
|
const maxSeq = Number(msgs[msgs.length - 1].seq);
|
|
161
219
|
await c.query(`UPDATE ${MAILBOX_TABLE} SET lease_owner = ?, lease_expires_at_ms = ?, lease_max_seq = ? WHERE scope_key = ? AND handle = ?`, [owner, now + ttlMs, maxSeq, scope, handle]);
|
|
162
|
-
return { messages: msgs.
|
|
220
|
+
return { messages: rowsToMessages(msgs, scope, handle, this.logger), maxSeq };
|
|
163
221
|
});
|
|
164
222
|
}
|
|
165
223
|
async ack(scope, handle, owner, upToSeq) {
|
|
@@ -203,8 +261,8 @@ export class TiDBMailboxStore {
|
|
|
203
261
|
const [newest] = (await c.query(`SELECT sent_at_ms FROM ${MAILBOX_MSG_TABLE} WHERE scope_key = ? AND handle = ? ORDER BY sent_at_ms DESC LIMIT 1 FOR UPDATE`, [scope, h]));
|
|
204
262
|
if (newest.length === 0 || Number(newest[0].sent_at_ms) >= cutoff)
|
|
205
263
|
continue;
|
|
206
|
-
await c.query(`DELETE FROM ${MAILBOX_TABLE} WHERE scope_key = ? AND handle = ?`, [scope, h]);
|
|
207
264
|
await c.query(`DELETE FROM ${MAILBOX_MSG_TABLE} WHERE scope_key = ? AND handle = ?`, [scope, h]);
|
|
265
|
+
await c.query(`UPDATE ${MAILBOX_TABLE} SET lease_owner = NULL, lease_expires_at_ms = NULL, lease_max_seq = NULL WHERE scope_key = ? AND handle = ?`, [scope, h]);
|
|
208
266
|
dropped++;
|
|
209
267
|
}
|
|
210
268
|
return dropped;
|
|
@@ -213,8 +271,11 @@ export class TiDBMailboxStore {
|
|
|
213
271
|
}
|
|
214
272
|
export class PgMailboxStore {
|
|
215
273
|
pool;
|
|
216
|
-
|
|
274
|
+
logger;
|
|
275
|
+
crossProcessSafe = true;
|
|
276
|
+
constructor(pool, logger) {
|
|
217
277
|
this.pool = pool;
|
|
278
|
+
this.logger = logger;
|
|
218
279
|
}
|
|
219
280
|
async tx(fn) {
|
|
220
281
|
const c = await this.pool.connect();
|
|
@@ -245,6 +306,7 @@ export class PgMailboxStore {
|
|
|
245
306
|
assertKeyBytes("scope", scope);
|
|
246
307
|
assertKeyBytes("handle", handle);
|
|
247
308
|
const hopChain = encodeHopChain(msg.hopChain);
|
|
309
|
+
const peerMeta = encodePeerMeta(msg.peerMeta);
|
|
248
310
|
return this.tx(async (c) => {
|
|
249
311
|
await c.query(`INSERT INTO ${MAILBOX_TABLE} (scope_key, handle, scope, next_seq) VALUES ($1, $2, $3, 1) ON CONFLICT (scope_key, handle) DO NOTHING`, [
|
|
250
312
|
scope,
|
|
@@ -262,7 +324,7 @@ export class PgMailboxStore {
|
|
|
262
324
|
}
|
|
263
325
|
const seq = Number(rows[0].next_seq);
|
|
264
326
|
await c.query(`UPDATE ${MAILBOX_TABLE} SET next_seq = $1 WHERE scope_key = $2 AND handle = $3`, [seq + 1, scope, handle]);
|
|
265
|
-
await c.query(`INSERT INTO ${MAILBOX_MSG_TABLE} (scope_key, handle, seq, from_id, content, sent_at_ms, hop_chain) VALUES ($1, $2, $3, $4, $5, $6, $7)`, [scope, handle, seq, msg.from ?? null, content, msg.sentAt, hopChain]);
|
|
327
|
+
await c.query(`INSERT INTO ${MAILBOX_MSG_TABLE} (scope_key, handle, seq, from_id, content, sent_at_ms, hop_chain, peer_meta) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, [scope, handle, seq, msg.from ?? null, content, msg.sentAt, hopChain, peerMeta]);
|
|
266
328
|
return seq;
|
|
267
329
|
});
|
|
268
330
|
}
|
|
@@ -275,12 +337,12 @@ export class PgMailboxStore {
|
|
|
275
337
|
const box = boxes[0];
|
|
276
338
|
if (box.lease_owner !== null && Number(box.lease_expires_at_ms) > now && String(box.lease_owner) !== owner)
|
|
277
339
|
return null;
|
|
278
|
-
const { rows: msgs } = await c.query(`SELECT seq, from_id, content, sent_at_ms, hop_chain FROM ${MAILBOX_MSG_TABLE} WHERE scope_key = $1 AND handle = $2 ORDER BY seq FOR UPDATE`, [scope, handle]);
|
|
340
|
+
const { rows: msgs } = await c.query(`SELECT seq, from_id, content, sent_at_ms, hop_chain, peer_meta FROM ${MAILBOX_MSG_TABLE} WHERE scope_key = $1 AND handle = $2 ORDER BY seq FOR UPDATE`, [scope, handle]);
|
|
279
341
|
if (msgs.length === 0)
|
|
280
342
|
return null;
|
|
281
343
|
const maxSeq = Number(msgs[msgs.length - 1].seq);
|
|
282
344
|
await c.query(`UPDATE ${MAILBOX_TABLE} SET lease_owner = $1, lease_expires_at_ms = $2, lease_max_seq = $3 WHERE scope_key = $4 AND handle = $5`, [owner, now + ttlMs, maxSeq, scope, handle]);
|
|
283
|
-
return { messages: msgs
|
|
345
|
+
return { messages: rowsToMessages(msgs, scope, handle, this.logger), maxSeq };
|
|
284
346
|
});
|
|
285
347
|
}
|
|
286
348
|
async ack(scope, handle, owner, upToSeq) {
|
|
@@ -327,8 +389,8 @@ export class PgMailboxStore {
|
|
|
327
389
|
const { rows: newest } = await c.query(`SELECT sent_at_ms FROM ${MAILBOX_MSG_TABLE} WHERE scope_key = $1 AND handle = $2 ORDER BY sent_at_ms DESC LIMIT 1 FOR UPDATE`, [scope, h]);
|
|
328
390
|
if (newest.length === 0 || Number(newest[0].sent_at_ms) >= cutoff)
|
|
329
391
|
continue;
|
|
330
|
-
await c.query(`DELETE FROM ${MAILBOX_TABLE} WHERE scope_key = $1 AND handle = $2`, [scope, h]);
|
|
331
392
|
await c.query(`DELETE FROM ${MAILBOX_MSG_TABLE} WHERE scope_key = $1 AND handle = $2`, [scope, h]);
|
|
393
|
+
await c.query(`UPDATE ${MAILBOX_TABLE} SET lease_owner = NULL, lease_expires_at_ms = NULL, lease_max_seq = NULL WHERE scope_key = $1 AND handle = $2`, [scope, h]);
|
|
332
394
|
dropped++;
|
|
333
395
|
}
|
|
334
396
|
return dropped;
|
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
* ⑨⑩([ref],2026-08-31)把两个最大的 wire 面也纳入:TaskSpec 按「server 有无写点」记 Wired/Unwired,
|
|
18
18
|
* TaskResult 按「转发/摘除」记 Forwarded/Stripped —— 谁改 resolve-spec.ts / runs.ts 的发射点谁同步本文件。
|
|
19
19
|
*/
|
|
20
|
-
import type { TaskNotificationPayload, BackgroundChildEvent, RosterEntry, AskRequest, CheckpointSummary, TaskEvent, TraceEvent, MailboxMessage,
|
|
20
|
+
import type { TaskNotificationPayload, BackgroundChildEvent, RosterEntry, AskRequest, CheckpointSummary, TaskEvent, TraceEvent, MailboxMessage, MailboxAppendMessage, TaskSpec, TaskResult } from "@sema-agent/core";
|
|
21
21
|
/** 编译期断言:T 必须收敛到 never(有残余键 = tsc 红)。 */
|
|
22
22
|
type AssertAllKeysHandled<T extends never> = T;
|
|
23
23
|
type NotificationProjected = "task_id" | "task_type" | "toolUseId" | "status" | "summary" | "result" | "output_file" | "usage" | "sessionId" | "seq" | "lines" | "stoppedBy" | "source" | "exitCode" | "partial" | "diagnostics" | "recentSteps" | "editedFiles" | "resumable" | "completionId" | "error" | "errorCode" | "apiFailure" | "agentMessage" | "_sema_provenance";
|
|
24
|
-
type NotificationExcluded = "peer";
|
|
24
|
+
type NotificationExcluded = "peer" | "crossSessionMessage";
|
|
25
25
|
type _GuardNotification = AssertAllKeysHandled<Exclude<keyof TaskNotificationPayload, NotificationProjected | NotificationExcluded>>;
|
|
26
26
|
type BgNotifProjected = "taskId" | "sessionId" | "seq" | "status" | "summary" | "stoppedBy" | "resumable" | "recentSteps" | "editedFiles" | "usage" | "transcriptId" | "rootSessionId" | "parentTaskId" | "parentToolCallId" | "completionId";
|
|
27
27
|
type BgNotifExcluded = "kind" | "sessionScoped" | "owner" | "scope" | "description" | "agentType" | "name" | "currentAction" | "currentTool" | "parentSessionId" | "startedAt" | "workflowRunId" | "progressTaskId" | "progressParentTaskId" | "model";
|
|
@@ -33,10 +33,9 @@ type AskExcluded = "preview" | "principal" | "riskAxes" | "boundInputHash" | "is
|
|
|
33
33
|
type _GuardAsk = AssertAllKeysHandled<Exclude<keyof AskRequest, AskProjected | AskExcluded>>;
|
|
34
34
|
type TaskEventHandled = "text_delta" | "reasoning_delta" | "tool_start" | "tool_end" | "turn_end" | "compacted" | "diagnostics" | "message_committed" | "status" | "task_notification" | "task_progress" | "steering_injected" | "workspace_changed" | "done" | "context_usage" | "compaction_outcome" | "human_input" | "wiring_manifest" | "text_end";
|
|
35
35
|
type _GuardTaskEvent = AssertAllKeysHandled<Exclude<TaskEvent["type"], TaskEventHandled>>;
|
|
36
|
-
type MailboxProjected = "seq" | "from" | "content" | "sentAt" | "hopChain";
|
|
36
|
+
type MailboxProjected = "seq" | "from" | "content" | "sentAt" | "hopChain" | "peerMeta";
|
|
37
37
|
type _GuardMailbox = AssertAllKeysHandled<Exclude<keyof MailboxMessage, MailboxProjected>>;
|
|
38
|
-
type
|
|
39
|
-
type MailboxAppendProjected = "from" | "content" | "sentAt" | "hopChain";
|
|
38
|
+
type MailboxAppendProjected = "from" | "content" | "sentAt" | "hopChain" | "peerMeta";
|
|
40
39
|
type _GuardMailboxAppend = AssertAllKeysHandled<Exclude<keyof MailboxAppendMessage, MailboxAppendProjected>>;
|
|
41
40
|
type PermissionTraceKind = Extract<TraceEvent["kind"], `permission.${string}`>;
|
|
42
41
|
type PermissionTraceProjected = "permission.persisted_rule_allowed" | "permission.rule_store_unreadable";
|
|
@@ -49,7 +48,7 @@ type _GuardPermissionTraceNonEmpty = AssertAllKeysHandled<AssertNonEmpty<Permiss
|
|
|
49
48
|
type SummaryProjected = "sessionId" | "scope" | "createdAt" | "gateKind" | "severity" | "spentMicroUsd" | "deadline" | "hasBidiControls";
|
|
50
49
|
type SummaryExcluded = "token" | "toolInput" | "toolName" | "toolCallId" | "preview" | "principal" | "sourceTaskId" | "contentKind" | "restoreMode" | "checkpointId" | "previewWithheld";
|
|
51
50
|
type _GuardInboxSummary = AssertAllKeysHandled<Exclude<keyof CheckpointSummary, SummaryProjected | SummaryExcluded>>;
|
|
52
|
-
type TaskSpecWired = "objective" | "principal" | "images" | "sessionId" | "requireExistingSession" | "oneShot" | "clientContext" | "resumeAt" | "resumeAtMode" | "model" | "compactionModel" | "thinking" | "systemPrompt" | "appendSystemPrompt" | "promptProvider" | "memory" | "suggestNextPrompts" | "restoreFiles" | "acceptPartialRestore" | "rewindFilesTo" | "tools" | "excludeTools" | "deferTools" | "toolMaterializeStrategy" | "promptProfile" | "toolPolicy" | "onAsk" | "onQuestion" | "hooks" | "durableApproval" | "resourceSuspend" | "resilience" | "finalVerification" | "checkpointStore" | "handsReadOnly" | "memoryPersistenceCapable" | "additionalDirectories" | "additionalReadDirectories" | "enablePlanMode" | "interactiveTools" | "enableFork" | "shellGate" | "mcp" | "a2a" | "skills" | "backgroundScope" | "envFacts" | "limits" | "configOverrides" | "forwardSubagentEvents" | "retainSubagentSessions" | "retainBackgroundProcesses" | "outputSchema" | "outputRetries" | "compaction" | "attachments" | "selfOrchestration" | "getApiKeyAndHeaders" | "interactionPosture" | "agents" | "taskId" | "signal" | "preemptSignal" | "modelRole" | "enableBlockedReport" | "maxSuspends";
|
|
51
|
+
type TaskSpecWired = "objective" | "principal" | "images" | "sessionId" | "requireExistingSession" | "oneShot" | "clientContext" | "resumeAt" | "resumeAtMode" | "model" | "compactionModel" | "thinking" | "systemPrompt" | "appendSystemPrompt" | "promptProvider" | "memory" | "suggestNextPrompts" | "restoreFiles" | "acceptPartialRestore" | "rewindFilesTo" | "tools" | "excludeTools" | "deferTools" | "toolMaterializeStrategy" | "promptProfile" | "toolPolicy" | "onAsk" | "onQuestion" | "hooks" | "durableApproval" | "resourceSuspend" | "resilience" | "finalVerification" | "checkpointStore" | "handsReadOnly" | "memoryPersistenceCapable" | "additionalDirectories" | "additionalReadDirectories" | "enablePlanMode" | "interactiveTools" | "enableFork" | "shellGate" | "mcp" | "a2a" | "skills" | "backgroundScope" | "envFacts" | "limits" | "configOverrides" | "forwardSubagentEvents" | "retainSubagentSessions" | "retainBackgroundProcesses" | "outputSchema" | "outputRetries" | "compaction" | "attachments" | "selfOrchestration" | "getApiKeyAndHeaders" | "interactionPosture" | "agents" | "taskId" | "signal" | "preemptSignal" | "modelRole" | "enableBlockedReport" | "maxSuspends" | "autoModeRequested";
|
|
53
52
|
type TaskSpecUnwired = "actor" | "roles" | "lspManager" | "tracer" | "readFace" | "readDenyPatterns" | "interactiveQuestionFallback" | "alwaysLoadTools" | "restoreGatedTools" | "deferSelfResolve" | "basePolicyForResumeEdit" | "lspDiagnostics" | "streamingToolExecution" | "toolResultThresholdChars";
|
|
54
53
|
type _GuardTaskSpec = AssertAllKeysHandled<Exclude<keyof TaskSpec, TaskSpecWired | TaskSpecUnwired>>;
|
|
55
54
|
type _GuardTaskSpecReverse = AssertAllKeysHandled<Exclude<TaskSpecWired | TaskSpecUnwired, keyof TaskSpec>>;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* 同文件 G1 e2e 的全码扇出夹具、以及契约文档附录 D.3 那张表(含**小节标题里的码数** —— 那道门本批
|
|
17
17
|
* 刚立,立完就在下一次加码时自己咬住了)。
|
|
18
18
|
*/
|
|
19
|
-
export declare const ENGINE_NOTICE_WIRE_CODES: readonly ["memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "task.turn_interrupted", "steering.parked_input_blocked", "task.user_steer_undrained", "task.user_followup_undrained", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "task.halt_unconsumed", "task.late_approval"];
|
|
19
|
+
export declare const ENGINE_NOTICE_WIRE_CODES: readonly ["memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "task.turn_interrupted", "steering.parked_input_blocked", "task.user_steer_undrained", "task.user_followup_undrained", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "task.halt_unconsumed", "task.late_approval", "config.durable_gate_unavailable", "peer.inbound_disposition"];
|
|
20
20
|
export type EngineNoticeWireCode = (typeof ENGINE_NOTICE_WIRE_CODES)[number];
|
|
21
21
|
/** 白名单谓词(单点):路由与门都读这一个,不许第二处手抄码串。 */
|
|
22
22
|
export declare function isEngineNoticeWireCode(code: string): code is EngineNoticeWireCode;
|
|
@@ -15,6 +15,8 @@ export const ENGINE_NOTICE_WIRE_CODES = [
|
|
|
15
15
|
"memory.capture_optout_unpersisted",
|
|
16
16
|
"task.halt_unconsumed",
|
|
17
17
|
"task.late_approval",
|
|
18
|
+
"config.durable_gate_unavailable",
|
|
19
|
+
"peer.inbound_disposition",
|
|
18
20
|
];
|
|
19
21
|
const WIRE_CODES = new Set(ENGINE_NOTICE_WIRE_CODES);
|
|
20
22
|
export function isEngineNoticeWireCode(code) {
|
package/dist/trace/project.d.ts
CHANGED
|
@@ -343,8 +343,47 @@ export declare function workspaceChangedEventData(ev: {
|
|
|
343
343
|
* ⚠️ 与 `storeWired` **不同**:本仓这两条至今未接(`RunnerDeps.permissionRuleSyncWired` /
|
|
344
344
|
* `permissionRuleOrg` 全树零声明点,2026-08-10 [ref] 复扫亲验)⇒ 今天恒 `false`,这是**真值**不是
|
|
345
345
|
* 缺席。哪天接上其中任一条,连本句一起改 —— 这一行是它的登记处。
|
|
346
|
+
* · `modelGate`(可选段 `{ class, removed[], restore }`;core fc52b4d4 [ref] / [ref] 片2b,[ref] 提货件,
|
|
347
|
+
* 黑板 [ref] @server①)—— **裁:租户可见**。它是 per-session 的**读面**:「我这条 session 因为工具-模型门卸掉了
|
|
348
|
+
* 哪些工具」——与 operator 通告 `config.tool_model_gate_removed` **同决策同源**(core 从同一张 trim 表铸两面),
|
|
349
|
+
* 但通告归 operator 车道(engine_notice 白名单,附录 D),本段归租户:消费端(cli L-70 [ref])要在**首帧一次读**
|
|
350
|
+
* 里知道自己少了哪些工具、怎么恢复,否则只能等到调用被拒才发现。三键都是引擎派生值:`class` = 合并 gate 表的 class 行键
|
|
351
|
+
* (prepare-task 对表外 class 响亮拒),`removed` = 工具 wire 名(core 表键,非用户内容),`restore` = core 铸的一句恢复指引(引擎文案)
|
|
352
|
+
* ⇒ 不脱敏、逐字。多 class 同卸 ⇒ core 报首 class + removed **并集**(core 合并律;本仓不按 class 过滤)。
|
|
353
|
+
* 仅本 run 有门卸时在场:**缺席 = 本 run 没卸**,不铸空段、不铸 `removed: []`。畸形(三键任一缺席/形不合)
|
|
354
|
+
* ⇒ 整段按缺席 —— 不铸半段,半段会被读成「只卸了这些」。它不构成 governance 四布尔的侧信道(门卸由
|
|
355
|
+
* 模型/门 class 决定,与 lockedConfig/compliance/memoryAdmission/retention 无函数关系)。
|
|
356
|
+
* · `autoMode`(`{ armed: boolean, reason: AutoModeArmReason }`;core 7.3.1 [ref],147③ 提货件,黑板 [ref])
|
|
357
|
+
* —— **裁:租户可见**。它是 per-leg 的**读面**:「我这条腿的 auto 模式武装了吗?没武装是卡在哪一臂?」
|
|
358
|
+
* ① **同主体同受众**:本仓早已把同一件事投在租户面上 —— `/v1/capabilities.permissionModeAuto.{armed,reason}`
|
|
359
|
+
* ([ref],7.57.0 已发)答的是「**若此刻提交**会不会武装」,本段答的是「**这条真跑起来的腿**武装了吗」;
|
|
360
|
+
* 同一个主体(调用方自己的 run)、同一条轴,只差事前/事后。剥掉它,壳就只能拿事前预测冒充事后事实;
|
|
361
|
+
* ② **两个值都是引擎派生的闭形**:`armed` 布尔,`reason` 是 core 的闭集词(`AUTO_MODE_ARM_REASONS`:
|
|
362
|
+
* armed / no_intent / no_face / denied / resolver_fault / latch_open)—— 非用户内容,不脱敏、逐字;
|
|
363
|
+
* ③ **不是 governance 侧信道**:它描述的是**本腿**的武装结论,与 lockedConfig/compliance/memoryAdmission/
|
|
364
|
+
* retention 四位无函数关系;而 `denied` 这一词所披露的「组织把这一位关了」在 [ref] 读面上**本就已经**
|
|
365
|
+
* 对同一租户可见(`org_denied`),本段没有扩大任何受众。
|
|
366
|
+
* 🔴 **逐字透传,不映射**:core 的六词与本仓 [ref] 读面的六词(`AUTO_MODE_UNARMED_REASONS`)**刻意不归一**
|
|
367
|
+
* —— 两张表回答的是两个问题、粒度也不同(core 的 `denied` 一词对应本仓的 `org_denied` / `local_denied`
|
|
368
|
+
* 两词:core 只看合成后的 caps deny 位,本仓分得出这个 false 是 center 下发的还是本地 env 写的)。
|
|
369
|
+
* ⚠️ **`settings_denied` 不折进 `denied`,它折进 `no_intent`**(codex 车BO r1 [medium] 验真后纠):
|
|
370
|
+
* settings 层的 kill-switch 折的是**模式**(`effectivePermissionMode`:auto → default),于是 resolve-spec
|
|
371
|
+
* 阶段④ 压根不写意图座 `TaskSpec.autoModeRequested` ⇒ core 的武装谓词第一项就落空;而本地 env 腿折的是
|
|
372
|
+
* **caps**(`applyLocalAutoModeDeny`),模式不动、座照写 ⇒ core 才报 `denied`。两条腿在 manifest 上
|
|
373
|
+
* **分不开**,要分辨得读 `/v1/capabilities`。对照表与保鲜钉在 `test/wiring-manifest-projection.test.ts`
|
|
374
|
+
* 的 147③ 节,分岔的行为面格在 `test/permission-mode-shell-gate-translation.test.ts`;
|
|
375
|
+
* 本函数**只透传**,一个字都不换。
|
|
376
|
+
* ⚠️ 缺席 ≠ 「不适用」:core 顶注逐字「absence here means an older mint or an external derivation, never
|
|
377
|
+
* "not applicable"」⇒ 缺席时**不铸** `armed:false`(那会把「老 mint 写的行」谎报成「这条腿确实没武装」)。
|
|
378
|
+
* ⚠️ **自相矛盾按缺席**:core 把 `armed ⇔ reason === "armed"` 写成段内不变量并在 derive 点拒(`readAutoModeFact`
|
|
379
|
+
* 抛)。账本回放/第三方 producer 送来的矛盾对若原样上 wire,按 `armed` 读与按 `reason` 读的消费端会得到
|
|
380
|
+
* **相反**的答案 —— 这条轴上(@cli `sema doctor permissions` 正读它)两个答案比没答案更坏 ⇒ 整段不铸。
|
|
381
|
+
* 这不是改语义:它与本函数「畸形值一律按缺席」是同一条规则,而这条不变量是 core 自己写的段内契约。
|
|
346
382
|
* 未来 core 加一段而它没有 audience 标签时,**先在这里补一条裁定再决定挑不挑键** —— 段级完备性钉
|
|
347
|
-
* (test/wiring-manifest-projection.test.ts)
|
|
383
|
+
* (test/wiring-manifest-projection.test.ts)会在那一刻先把人拦下来 —— ⚠️ **但那条钉是夹具驱动的**:它只看
|
|
384
|
+
* `deriveWiringManifest(EFFECTIVE_FACTS)` 真铸出来的段,facts 不带的可选段(147③ 的 `autoMode` 就是)它一格
|
|
385
|
+
* 都红不了(2026-09-04 实测:该钉 26/26 全绿,红出 `autoMode` 的是编译期门)。段级 + 成员级的**编译期**门在
|
|
386
|
+
* `core-keyset-guard.ts` ⑥b —— 那才是「core 加了段/加了成员」的可靠红点,两道并用缺一不可。
|
|
348
387
|
*/
|
|
349
388
|
export declare function wiringManifestEventData(ev: {
|
|
350
389
|
manifest?: unknown;
|
|
@@ -410,8 +449,9 @@ export declare function humanInputEventData(ev: {
|
|
|
410
449
|
}): Record<string, unknown>;
|
|
411
450
|
/** S4 (SILENT-FALLBACK P0-a): the durable `brain_status` observation row, shared by the bg + resume append
|
|
412
451
|
* sites. core marks the live `status` frame EPHEMERAL (never replayed by core on resume) — this row is the
|
|
413
|
-
* SERVICE's own observation record (why a turn stalled: rate_limited/retrying/reconnecting/circuit_open
|
|
414
|
-
*
|
|
452
|
+
* SERVICE's own observation record (why a turn stalled: rate_limited/retrying/reconnecting/circuit_open, and
|
|
453
|
+
* since core 7.3.1 / [ref] the pre-failure `waiting_first_token` wait), not a core event replay.
|
|
454
|
+
* Whitelist: the closed phase union verbatim + neutral bounded detail (redacted) + the bounded numeric seats. */
|
|
415
455
|
export declare function brainStatusEventData(ev: {
|
|
416
456
|
phase: string;
|
|
417
457
|
detail?: string;
|
|
@@ -422,6 +462,8 @@ export declare function brainStatusEventData(ev: {
|
|
|
422
462
|
errClass?: string;
|
|
423
463
|
retryAtMs?: number;
|
|
424
464
|
errorStatus?: number;
|
|
465
|
+
elapsedMs?: number;
|
|
466
|
+
timeoutMs?: number;
|
|
425
467
|
eventId?: string;
|
|
426
468
|
parentToolCallId?: string;
|
|
427
469
|
}): Record<string, unknown>;
|
package/dist/trace/project.js
CHANGED
|
@@ -250,6 +250,25 @@ export function wiringManifestEventData(ev) {
|
|
|
250
250
|
...(bool(rulesIn.syncWired) !== undefined ? { syncWired: rulesIn.syncWired } : {}),
|
|
251
251
|
...(bool(rulesIn.orgGoverned) !== undefined ? { orgGoverned: rulesIn.orgGoverned } : {}),
|
|
252
252
|
});
|
|
253
|
+
const gateIn = sec(m.modelGate);
|
|
254
|
+
const modelGate = (() => {
|
|
255
|
+
if (gateIn === undefined)
|
|
256
|
+
return undefined;
|
|
257
|
+
const cls = str(gateIn.class);
|
|
258
|
+
const restore = str(gateIn.restore);
|
|
259
|
+
const removed = Array.isArray(gateIn.removed) && gateIn.removed.every((r) => typeof r === "string") ? [...gateIn.removed] : undefined;
|
|
260
|
+
return cls !== undefined && restore !== undefined && removed !== undefined ? { class: cls, removed, restore } : undefined;
|
|
261
|
+
})();
|
|
262
|
+
const autoIn = sec(m.autoMode);
|
|
263
|
+
const autoMode = (() => {
|
|
264
|
+
if (autoIn === undefined)
|
|
265
|
+
return undefined;
|
|
266
|
+
const armed = bool(autoIn.armed);
|
|
267
|
+
const reason = str(autoIn.reason);
|
|
268
|
+
if (armed === undefined || reason === undefined)
|
|
269
|
+
return undefined;
|
|
270
|
+
return armed === (reason === "armed") ? { armed, reason } : undefined;
|
|
271
|
+
})();
|
|
253
272
|
return {
|
|
254
273
|
...(num(m.schemaVersion) !== undefined ? { schemaVersion: m.schemaVersion } : {}),
|
|
255
274
|
...(str(legIn.kind) !== undefined ? { leg: { kind: legIn.kind } } : {}),
|
|
@@ -261,6 +280,8 @@ export function wiringManifestEventData(ev) {
|
|
|
261
280
|
...(str(sessionIn.store) !== undefined ? { session: { store: sessionIn.store } } : {}),
|
|
262
281
|
...(fleet !== undefined ? { fleet } : {}),
|
|
263
282
|
...(permissionRules !== undefined ? { permissionRules } : {}),
|
|
283
|
+
...(modelGate !== undefined ? { modelGate } : {}),
|
|
284
|
+
...(autoMode !== undefined ? { autoMode } : {}),
|
|
264
285
|
...identityFields(ev),
|
|
265
286
|
};
|
|
266
287
|
}
|
|
@@ -314,6 +335,8 @@ export function brainStatusEventData(ev) {
|
|
|
314
335
|
...(num(ev.maxRetries) ? { maxRetries: ev.maxRetries } : {}),
|
|
315
336
|
...(num(ev.retryInMs) ? { retryInMs: ev.retryInMs } : {}),
|
|
316
337
|
...(num(ev.retryAtMs) ? { retryAtMs: ev.retryAtMs } : {}),
|
|
338
|
+
...(num(ev.elapsedMs) ? { elapsedMs: ev.elapsedMs } : {}),
|
|
339
|
+
...(num(ev.timeoutMs) ? { timeoutMs: ev.timeoutMs } : {}),
|
|
317
340
|
...(num(ev.errorStatus) ? { errorStatus: ev.errorStatus } : {}),
|
|
318
341
|
...(typeof ev.errClass === "string" && ev.errClass.length > 0 ? { errClass: ev.errClass } : {}),
|
|
319
342
|
...identityFields(ev),
|
package/dist/trace/redact.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
2
2
|
const REDACTION_TOKEN_RE_SRC = String.raw `«redacted(?::[a-z-]{1,16}){0,2}»`;
|
|
3
3
|
const FULL_SECRET_PATTERNS = [
|
|
4
|
-
[/-----BEGIN[^-]
|
|
4
|
+
[/-----BEGIN[^-](?:[^-](?:[^-]|-(?!-))*)?PRIVATE KEY(?: BLOCK)?-----[\s\S]{0,8192}?-----END[^-](?:[^-](?:[^-]|-(?!-))*)?PRIVATE KEY(?: BLOCK)?-----/g, "«redacted:private-key»"],
|
|
5
5
|
[/---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----[\s\S]{0,8192}?---- END SSH2 ENCRYPTED PRIVATE KEY ----/g, "«redacted:private-key»"],
|
|
6
6
|
[/\bsk-[A-Za-z0-9_-]{16,}\b/g, "«redacted:key»"],
|
|
7
7
|
[/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}\b/g, "«redacted:gh-token»"],
|
|
@@ -220,8 +220,12 @@ function isPemFenceLabel(s, from, to) {
|
|
|
220
220
|
return false;
|
|
221
221
|
if (!s.startsWith(PEM_KEY_WORD, end - PEM_KEY_WORD.length))
|
|
222
222
|
return false;
|
|
223
|
-
|
|
224
|
-
|
|
223
|
+
if (s.charCodeAt(from) === 45)
|
|
224
|
+
return false;
|
|
225
|
+
if (s.charCodeAt(from + 1) === 45)
|
|
226
|
+
return false;
|
|
227
|
+
for (let i = from + 1; i < to; i++)
|
|
228
|
+
if (s.charCodeAt(i) === 45 && s.charCodeAt(i + 1) === 45)
|
|
225
229
|
return false;
|
|
226
230
|
return true;
|
|
227
231
|
}
|
|
@@ -11,7 +11,16 @@
|
|
|
11
11
|
* (它是渲染判别,不是 lane 词)。
|
|
12
12
|
*/
|
|
13
13
|
import type { SemaProvenance } from "@sema-agent/core";
|
|
14
|
+
/** wire 放行的 lane 词(PROJECTED)。 */
|
|
14
15
|
export declare const SEMA_PROVENANCE_KINDS: readonly ["agent_message"];
|
|
16
|
+
/**
|
|
17
|
+
* 有意**不**放行的 lane 词(EXCLUDED,逐词记账;运行期落到「丢整键」臂 —— 与闭集外的未知词同臂,但这里的词是
|
|
18
|
+
* core 词表**内**的、经人裁定不上 wire 的):
|
|
19
|
+
* · `cross_session_message` —— core fc52b4d4 [ref] §4.1(session-box drain 铸;`from` 是对端**地址**、`peerMeta`
|
|
20
|
+
* 携 typed peer 记录)。[ref] 候:本仓不可达(`RunnerDeps.peerDirectory` 零声明点)+ 注入帧渲染形候 [ref] @server③
|
|
21
|
+
* 表态;与 core-keyset-guard.ts ① 的 `crossSessionMessage` / ①-nested 的 `peerMeta` 同批同判,接线时三处同翻。
|
|
22
|
+
*/
|
|
23
|
+
export declare const SEMA_PROVENANCE_KINDS_EXCLUDED: readonly ["cross_session_message"];
|
|
15
24
|
/** 本仓两条腿入参的**形**(不是 core 类型:入参是运行期载荷,`kind` 先当 string 收、经闭集才成 typed)。 */
|
|
16
25
|
export interface SemaProvenanceInput {
|
|
17
26
|
kind: string;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export const SEMA_PROVENANCE_KINDS = ["agent_message"];
|
|
2
|
+
export const SEMA_PROVENANCE_KINDS_EXCLUDED = ["cross_session_message"];
|
|
2
3
|
const _semaProvenanceExhaustive = true;
|
|
3
4
|
void _semaProvenanceExhaustive;
|
|
5
|
+
const _semaProvenanceNoDoubleBook = true;
|
|
6
|
+
void _semaProvenanceNoDoubleBook;
|
|
4
7
|
export function semaProvenanceOrAbsent(p) {
|
|
5
8
|
if (!SEMA_PROVENANCE_KINDS.includes(p.kind))
|
|
6
9
|
return undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.58.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,8 +54,8 @@
|
|
|
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.
|
|
58
|
-
"@sema-agent/settings-schema": "1.
|
|
57
|
+
"@sema-agent/core": "7.3.1",
|
|
58
|
+
"@sema-agent/settings-schema": "1.5.0",
|
|
59
59
|
"e2b": "^2.28.0",
|
|
60
60
|
"libsodium-wrappers": "^0.8.4",
|
|
61
61
|
"mysql2": "^3.22.4",
|