@sema-agent/server 7.79.3 → 7.80.1
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/stage-07-capability-layer.js +2 -1
- package/dist/capabilities/skills.d.ts +10 -0
- package/dist/capabilities/skills.js +3 -1
- package/dist/http/admission.js +3 -1
- package/dist/http/dispatch.js +28 -8
- package/dist/http/resume-legs.js +4 -3
- package/dist/http/route-ctx.d.ts +101 -4
- package/dist/http/route-ctx.js +4 -0
- package/dist/http/route-table.d.ts +71 -43
- package/dist/http/route-table.js +111 -187
- package/dist/http/routes/a2a-serve.d.ts +18 -3
- package/dist/http/routes/a2a-serve.js +25 -23
- package/dist/http/routes/admin-config-refresh.d.ts +9 -2
- package/dist/http/routes/admin-config-refresh.js +32 -31
- package/dist/http/routes/admin-drain.d.ts +9 -2
- package/dist/http/routes/admin-drain.js +32 -31
- package/dist/http/routes/admin-memory-optout.d.ts +28 -2
- package/dist/http/routes/admin-memory-optout.js +27 -40
- package/dist/http/routes/adoption.d.ts +15 -2
- package/dist/http/routes/adoption.js +52 -52
- package/dist/http/routes/agents-roster.d.ts +9 -2
- package/dist/http/routes/agents-roster.js +82 -80
- package/dist/http/routes/approvals-assistant.d.ts +58 -5
- package/dist/http/routes/approvals-assistant.js +38 -39
- package/dist/http/routes/attachments.d.ts +22 -2
- package/dist/http/routes/attachments.js +81 -72
- package/dist/http/routes/capabilities.d.ts +21 -2
- package/dist/http/routes/capabilities.js +174 -170
- package/dist/http/routes/config-catalog.d.ts +9 -2
- package/dist/http/routes/config-catalog.js +28 -31
- package/dist/http/routes/devices.d.ts +24 -2
- package/dist/http/routes/devices.js +19 -21
- package/dist/http/routes/diagnostics.d.ts +9 -2
- package/dist/http/routes/diagnostics.js +41 -43
- package/dist/http/routes/fleet.d.ts +11 -2
- package/dist/http/routes/fleet.js +23 -22
- package/dist/http/routes/images.d.ts +63 -9
- package/dist/http/routes/images.js +53 -46
- package/dist/http/routes/leader.d.ts +20 -2
- package/dist/http/routes/leader.js +43 -40
- package/dist/http/routes/memory-bundle.d.ts +15 -2
- package/dist/http/routes/memory-bundle.js +38 -38
- package/dist/http/routes/memory-compliance.d.ts +16 -4
- package/dist/http/routes/memory-compliance.js +44 -45
- package/dist/http/routes/memory-consolidation.d.ts +14 -2
- package/dist/http/routes/memory-consolidation.js +66 -66
- package/dist/http/routes/memory-origin.d.ts +23 -4
- package/dist/http/routes/memory-origin.js +65 -65
- package/dist/http/routes/memory-policy.d.ts +21 -2
- package/dist/http/routes/memory-policy.js +119 -115
- package/dist/http/routes/notify-wake.d.ts +21 -11
- package/dist/http/routes/notify-wake.js +118 -116
- package/dist/http/routes/observability.d.ts +16 -2
- package/dist/http/routes/observability.js +54 -51
- package/dist/http/routes/retention-ops.d.ts +19 -2
- package/dist/http/routes/retention-ops.js +20 -22
- package/dist/http/routes/rules.d.ts +36 -2
- package/dist/http/routes/rules.js +22 -24
- package/dist/http/routes/run-memory-capture.d.ts +10 -3
- package/dist/http/routes/run-memory-capture.js +84 -84
- package/dist/http/routes/runs.d.ts +102 -22
- package/dist/http/routes/runs.js +79 -96
- package/dist/http/routes/session-sync.d.ts +41 -2
- package/dist/http/routes/session-sync.js +26 -24
- package/dist/http/routes/sessions-list.d.ts +16 -2
- package/dist/http/routes/sessions-list.js +78 -75
- package/dist/http/routes/sessions.d.ts +77 -2
- package/dist/http/routes/sessions.js +50 -47
- package/dist/http/routes/shared-memory.d.ts +19 -2
- package/dist/http/routes/shared-memory.js +65 -63
- package/dist/http/routes/side-query.d.ts +11 -2
- package/dist/http/routes/side-query.js +81 -79
- package/dist/http/routes/tasks.d.ts +17 -2
- package/dist/http/routes/tasks.js +18 -11
- package/dist/http/routes/trace-usage.d.ts +40 -2
- package/dist/http/routes/trace-usage.js +46 -35
- package/dist/http/routes/workflows.d.ts +35 -3
- package/dist/http/routes/workflows.js +29 -29
- package/dist/http/wire-types.d.ts +15 -9
- package/dist/parked-decide.d.ts +9 -0
- package/dist/parked-decide.js +3 -3
- package/dist/run-local.js +2 -1
- package/dist/spec-fields.js +33 -30
- package/dist/task-cwd.d.ts +21 -0
- package/dist/task-cwd.js +3 -0
- package/dist/trace/core-keyset-guard.d.ts +6 -1
- package/dist/trace/project.d.ts +20 -0
- package/dist/trace/project.js +15 -1
- package/package.json +2 -2
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* cut: ONLY TaskRequestBody (its field types are inline or core imports — no other server.ts-local satellites);
|
|
7
7
|
* sibling request/response shapes can migrate here later on the same pattern.
|
|
8
8
|
*/
|
|
9
|
-
import type { A2aServerSpec, AgentDefinition, McpServerSpec } from "@sema-agent/core";
|
|
9
|
+
import type { A2aServerSpec, AgentDefinition, McpServerSpec, SkillSpec } from "@sema-agent/core";
|
|
10
10
|
export interface TaskRequestBody {
|
|
11
11
|
objective: string;
|
|
12
12
|
sessionId?: string;
|
|
@@ -98,14 +98,20 @@ export interface TaskRequestBody {
|
|
|
98
98
|
jobId?: string;
|
|
99
99
|
/** Personalization seam: the caller's OWN skills for this task, threaded into core's
|
|
100
100
|
* `TaskSpec.skills`. Merged AFTER the scenario's skills with SCENARIO-WINS on a name clash (the security
|
|
101
|
-
* baseline can never be shadowed by user content — see mergeUserSkills).
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
101
|
+
* baseline can never be shadowed by user content — see mergeUserSkills). User skills are user-authored
|
|
102
|
+
* content in the user's OWN principal-isolated task (self-harm surface, not privilege widening);
|
|
103
|
+
* scenario/center skills remain the only trusted layer.
|
|
104
|
+
*
|
|
105
|
+
* 🔴 **类型 = core 的 `SkillSpec` 本体,不是它的手抄子集**(S-377):这里曾写死
|
|
106
|
+
* `{ name; description; content }` —— core 闭集六位里的三位,而 `manifest` / `files` / `baseDir`
|
|
107
|
+
* 三位在运行期一直是**收的**(受理门 `validateUserSkills` 不判未知键,`mergeUserSkills` 原样透传到
|
|
108
|
+
* `TaskSpec.skills`)。一份会漂的手抄 = 声明面比真受理面**窄**,SDK 消费方按声明写代码就以为发不了,
|
|
109
|
+
* 而 core 加键(7.20.0 [ref] 的 `baseDir`)时这里连红都不会红。改成 core 本体后,闭集由 tsc 看守。
|
|
110
|
+
* ⚠️ 受理**判据**(形/尺寸)仍在 `validateUserSkills` + `spec-fields.ts` 的 `SKILL_FIELD_SHAPES`——
|
|
111
|
+
* 类型说「哪些键存在」,门说「值合不合法」,两者不是一件事。
|
|
112
|
+
* (旧注里的「Caps: ≤10 skills, content ≤32KB each」两条帽早已随 [ref] / core 1.293 退役,一并纠正:
|
|
113
|
+
* 今天只剩 `content ≤ 1 MiB` 这一条 core 加载门孪生。) */
|
|
114
|
+
skills?: SkillSpec[];
|
|
109
115
|
/** §7 P0.5 (用户按需选像): the sandbox image PROFILE this task wants (e.g.
|
|
110
116
|
* "code-full" / "code-python"). An INTENT, never a digest — the service resolves it to a published digest with
|
|
111
117
|
* the caller's principal (fail-closed visibility re-admit) and applies it per-pod. Only supported on the k8s
|
package/dist/parked-decide.d.ts
CHANGED
|
@@ -260,6 +260,15 @@ export type WorkflowParkedDecidePlan = {
|
|
|
260
260
|
* 三句里持久数据那一句要的就是这个形:响亮拒,不做静默双读(读 `agents[]` 补一份身份正是被退役的双算)。
|
|
261
261
|
*
|
|
262
262
|
* 体上只带 `runId`(那是操作员唯一用得上的坐标;真源都读不出来,任何计数都是编的)。
|
|
263
|
+
*
|
|
264
|
+
* 🔴 **S-376(词域门,7.80.1):拒文逐字改,status / errorCode / 体键零变。** 旧句把本注里的内部机件整段
|
|
265
|
+
* 搬上了 wire(「the parks the run owns」「when it parks」「join your checkpoint to a park」「its own parked
|
|
266
|
+
* legs」)—— 那些名字调用方一个都拿不到。新句把承重的四件事一件不少地留着(不投递 / 卡仍 pending 什么都没
|
|
267
|
+
* 伪造 / 恢复动作 `Workflow({resumeFromRunId})` / 只有引擎也拒才是真恢复不了)。
|
|
268
|
+
* ⚠️ **新句刻意不提 `checkpointToken`**(codex 对抗复审 r1 指出、亲核属实):契约 D-1 逐字写着它是
|
|
269
|
+
* server-INTERNAL、不上 `/v1/approvals` 记录、合规 SDK 绝不发送 —— 让调用方去找一个它既没发也拿不到的
|
|
270
|
+
* 字段是错指路。新句说的是「无法确认这项待决审批属于这条 run」。判据与冻结钉见
|
|
271
|
+
* `test/api-error-text-lexicon.test.ts`。
|
|
263
272
|
*/
|
|
264
273
|
export declare function denyWorkflowParksUnreadable(origin: Extract<ParkOrigin, {
|
|
265
274
|
kind: "workflow_parks_unreadable";
|
package/dist/parked-decide.js
CHANGED
|
@@ -89,7 +89,7 @@ export function denyWorkflowParksUnreadable(origin) {
|
|
|
89
89
|
return {
|
|
90
90
|
status: 409,
|
|
91
91
|
body: {
|
|
92
|
-
error: "this
|
|
92
|
+
error: "this decision cannot be matched to the pending step it belongs to: this run's record was written by an older engine, or one of its entries is damaged, so it no longer says which pending steps the run owns — and that list is the only proof that the approval you are deciding belongs to this run. The decision is therefore NOT delivered, rather than answered with a receipt nothing can redeem: the approval card stays pending and nothing is forged. Resume the run itself instead — `Workflow({resumeFromRunId})` with the runId below — and the engine rebuilds that list from its own records as it admits the resume, after which deciding here works normally. Only if the engine also refuses that resume is the run truly unrecoverable",
|
|
93
93
|
errorCode: "decide.workflow_parks_unreadable",
|
|
94
94
|
runId: origin.runId,
|
|
95
95
|
},
|
|
@@ -102,7 +102,7 @@ export function planWorkflowParkedDecide(origin, req) {
|
|
|
102
102
|
deny: {
|
|
103
103
|
status: 400,
|
|
104
104
|
body: {
|
|
105
|
-
error: "remember:\"session\" is not supported for
|
|
105
|
+
error: "remember:\"session\" is not supported for this approval: accepting it here only means the decision was handed on for delivery, not that the engine has applied it — an exemption granted now could outlive a decision the engine later rejects. Re-issue the decide without `remember`; the decision itself is accepted normally that way.",
|
|
106
106
|
errorCode: "decide.workflow_remember_unsupported",
|
|
107
107
|
runId: origin.runId,
|
|
108
108
|
},
|
|
@@ -116,7 +116,7 @@ export function planWorkflowParkedDecide(origin, req) {
|
|
|
116
116
|
deny: {
|
|
117
117
|
status: 409,
|
|
118
118
|
body: {
|
|
119
|
-
error: "this
|
|
119
|
+
error: "this run was started on its own rather than from a session, so this deployment has nowhere to deliver the decision. Nothing was decided and the approval is still pending — resume the run yourself with the runId below and decide it there",
|
|
120
120
|
errorCode: "decide.workflow_host_unknown",
|
|
121
121
|
runId: origin.runId,
|
|
122
122
|
},
|
package/dist/run-local.js
CHANGED
|
@@ -23,6 +23,7 @@ import { hostExecutionEnvFactory } from "./plugins/remote-env-host.js";
|
|
|
23
23
|
import { executionLaneHomeDir } from "./execution-lane-caps.js";
|
|
24
24
|
import { makeLoadProjectMemory, makeProbeInstructionSources } from "./project-memory.js";
|
|
25
25
|
import { loadSkills } from "./capabilities/skills.js";
|
|
26
|
+
import { toolsRunOnThisHost } from "./task-cwd.js";
|
|
26
27
|
import { createRepoClient } from "./capabilities/repo-tools.js";
|
|
27
28
|
import { webSearchConfigFromEnv, createWebSearchBackend } from "./plugins/web-search.js";
|
|
28
29
|
import { buildScenarios, selectScenario, centerScenarios } from "./capabilities/scenarios.js";
|
|
@@ -424,7 +425,7 @@ export async function runLocal(argv, deps = {}) {
|
|
|
424
425
|
const subRunner = new Runner(subRunnerDeps);
|
|
425
426
|
const handslessRunner = new Runner(withoutExecutionEnv(runnerDeps));
|
|
426
427
|
const handslessSubRunner = new Runner(withoutExecutionEnv(subRunnerDeps));
|
|
427
|
-
const skills = loadSkills(config.skillsDir);
|
|
428
|
+
const skills = loadSkills(config.skillsDir, { toolsShareThisFilesystem: toolsRunOnThisHost(config) });
|
|
428
429
|
const repoClient = config.gitApiBaseUrl ? createRepoClient(config.gitApiKind, config.gitApiBaseUrl, config.gitApiToken) : undefined;
|
|
429
430
|
const webSearchCfg = webSearchConfigFromEnv();
|
|
430
431
|
const webSearch = webSearchCfg ? createWebSearchBackend(webSearchCfg) : undefined;
|
package/dist/spec-fields.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isThinkingLevel } from "@sema-agent/core";
|
|
2
2
|
import { taskWallClockSec } from "./task-workflow.js";
|
|
3
|
+
import { isValidCwd } from "./task-cwd.js";
|
|
3
4
|
export function normalizeSuggestNextPrompts(v) {
|
|
4
5
|
if (v === true)
|
|
5
6
|
return true;
|
|
@@ -65,45 +66,47 @@ const toolList = (v) => Array.isArray(v) &&
|
|
|
65
66
|
v.length <= MAX_AGENT_TOOLS &&
|
|
66
67
|
v.every((x) => typeof x === "string" && x.length > 0 && x.length <= MAX_AGENT_TOOL_NAME_CHARS);
|
|
67
68
|
const plainObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
68
|
-
const
|
|
69
|
-
if (!plainObject(
|
|
69
|
+
const skillManifestOk = (m) => {
|
|
70
|
+
if (!plainObject(m))
|
|
70
71
|
return false;
|
|
71
|
-
for (const k of Object.keys(
|
|
72
|
-
if (k !== "
|
|
72
|
+
for (const k of Object.keys(m)) {
|
|
73
|
+
if (k !== "allowTools" && k !== "allowPaths" && k !== "egress" && k !== "lineageId")
|
|
73
74
|
return false;
|
|
74
75
|
}
|
|
75
|
-
if (
|
|
76
|
+
if (!(Array.isArray(m.allowTools) && m.allowTools.every((x) => typeof x === "string")))
|
|
76
77
|
return false;
|
|
77
|
-
if (
|
|
78
|
+
if (m.allowPaths !== undefined && !(Array.isArray(m.allowPaths) && m.allowPaths.every((x) => typeof x === "string")))
|
|
78
79
|
return false;
|
|
79
|
-
if (
|
|
80
|
+
if (m.egress !== undefined && typeof m.egress !== "boolean")
|
|
80
81
|
return false;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
82
|
+
return typeof m.lineageId === "string" && m.lineageId.length > 0;
|
|
83
|
+
};
|
|
84
|
+
const skillFilesOk = (v) => Array.isArray(v) &&
|
|
85
|
+
v.every((f) => plainObject(f) && typeof f.path === "string" && f.path.length > 0 && typeof f.content === "string");
|
|
86
|
+
const SKILL_FIELD_SHAPES = {
|
|
87
|
+
name: { required: true, ok: (v) => typeof v === "string" && v.length > 0 },
|
|
88
|
+
description: { required: true, ok: (v) => typeof v === "string" },
|
|
89
|
+
content: { required: true, ok: (v) => typeof v === "string" && v.length > 0 && v.length <= MAX_AGENT_SKILL_CONTENT_CHARS },
|
|
90
|
+
manifest: { required: false, ok: skillManifestOk },
|
|
91
|
+
files: { required: false, ok: skillFilesOk },
|
|
92
|
+
baseDir: { required: false, ok: isValidCwd },
|
|
93
|
+
};
|
|
94
|
+
const skillOk = (s) => {
|
|
95
|
+
if (!plainObject(s))
|
|
96
|
+
return false;
|
|
97
|
+
for (const k of Object.keys(s)) {
|
|
98
|
+
if (!Object.hasOwn(SKILL_FIELD_SHAPES, k))
|
|
96
99
|
return false;
|
|
97
100
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (!plainObject(f))
|
|
103
|
-
return false;
|
|
104
|
-
if (typeof f.path !== "string" || f.path.length === 0 || typeof f.content !== "string")
|
|
101
|
+
for (const [k, shape] of Object.entries(SKILL_FIELD_SHAPES)) {
|
|
102
|
+
const v = s[k];
|
|
103
|
+
if (v === undefined) {
|
|
104
|
+
if (shape.required)
|
|
105
105
|
return false;
|
|
106
|
+
continue;
|
|
106
107
|
}
|
|
108
|
+
if (!shape.ok(v))
|
|
109
|
+
return false;
|
|
107
110
|
}
|
|
108
111
|
return true;
|
|
109
112
|
};
|
|
@@ -134,7 +137,7 @@ const TASK_AGENT_FIELD_SHAPES = {
|
|
|
134
137
|
allowTools: { desc: `an array of at most ${MAX_AGENT_TOOLS} non-empty strings of at most ${MAX_AGENT_TOOL_NAME_CHARS} characters each`, ok: toolList },
|
|
135
138
|
denyTools: { desc: `an array of at most ${MAX_AGENT_TOOLS} non-empty strings of at most ${MAX_AGENT_TOOL_NAME_CHARS} characters each`, ok: toolList },
|
|
136
139
|
skills: {
|
|
137
|
-
desc: "an array of { name, description, content } skill objects (optional manifest/files; files entries need { path, content }) — no count cap; the engine renders the listing within its own byte budget",
|
|
140
|
+
desc: "an array of { name, description, content } skill objects (optional manifest/files/baseDir; files entries need { path, content }; baseDir must be an absolute path with no \"..\" segment) — no count cap; the engine renders the listing within its own byte budget",
|
|
138
141
|
ok: (v) => Array.isArray(v) && v.every(skillOk),
|
|
139
142
|
},
|
|
140
143
|
background: { desc: "a boolean", ok: (v) => typeof v === "boolean" },
|
package/dist/task-cwd.d.ts
CHANGED
|
@@ -52,6 +52,27 @@ export declare function cwdHonored(config: {
|
|
|
52
52
|
};
|
|
53
53
|
requirePrincipal?: boolean;
|
|
54
54
|
}): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* S-377 —— 「**工具跑的那台机器**就是本进程这台吗」。回答的是一个**文件系统同一性**问题,不是权限问题:
|
|
57
|
+
* server 盘上的一条绝对路径(出厂 `SKILLS_DIR` 下某个技能自己的目录)对模型的工具**有没有意义**。
|
|
58
|
+
*
|
|
59
|
+
* · `host`(run-on-this-machine,无容器)⇒ 真;
|
|
60
|
+
* · `undefined`(in-process 单用户 lane)⇒ 真 —— 该 lane 根本没挂手(core 落 `StubExecutionEnv`),
|
|
61
|
+
* 路径对谁都不可达,但它在**文件系统同一性**这条轴上确实是同一台机器,判真让规则只剩一条;
|
|
62
|
+
* · 其余每一条(`k8s` / `e2b` / `ssh` / `adb` / `device` / `local-docker`)⇒ 假:工具的盘在别处,
|
|
63
|
+
* server 自己的路径在那边**不存在**,声明它等于给模型一条指向不存在文件的线索,还顺带把本机目录
|
|
64
|
+
* 布局说了出去。
|
|
65
|
+
*
|
|
66
|
+
* 🔴 **为什么不复用 {@link cwdHonored}**:那一只是**权限**判据(单用户 ∧ host),它的 `requirePrincipal`
|
|
67
|
+
* 那一半防的是「一个租户的输入把 agent 指向另一个租户的文件」。本谓词判的路径不是调用方给的,是**运维
|
|
68
|
+
* 自己烤进镜像**的出厂内容 —— 每个租户拿到的是同一份,没有跨租户面。把租户判据搭进来会让多租户 host
|
|
69
|
+
* 部署平白失去这条线索,那是**用错轴**(与 `deviceCwdHonored` 拒绝并进 `cwdHonored` 同一条理由)。
|
|
70
|
+
*/
|
|
71
|
+
export declare function toolsRunOnThisHost(config: {
|
|
72
|
+
remoteExec?: {
|
|
73
|
+
provider?: string;
|
|
74
|
+
};
|
|
75
|
+
}): boolean;
|
|
55
76
|
/**
|
|
56
77
|
* device lane 的**平行**判别式(device-executor-lane-v2 §4.3.3,车A-4)——「员工在自己设备的项目目录
|
|
57
78
|
* 里干活」是这条车道的存在理由,所以 caller cwd 必须被尊重。
|
package/dist/task-cwd.js
CHANGED
|
@@ -20,6 +20,9 @@ export function dirCoversPath(dir, target) {
|
|
|
20
20
|
export function cwdHonored(config) {
|
|
21
21
|
return config.remoteExec?.provider === "host" && config.requirePrincipal !== true;
|
|
22
22
|
}
|
|
23
|
+
export function toolsRunOnThisHost(config) {
|
|
24
|
+
return config.remoteExec === undefined || config.remoteExec.provider === undefined || config.remoteExec.provider === "host";
|
|
25
|
+
}
|
|
23
26
|
export function deviceCwdHonored(config) {
|
|
24
27
|
return config.remoteExec?.provider === "device";
|
|
25
28
|
}
|
|
@@ -59,7 +59,7 @@ type _GuardMailboxReverse = AssertAllKeysHandled<Exclude<MailboxProjected, keyof
|
|
|
59
59
|
type MailboxAppendProjected = "from" | "content" | "sentAt" | "hopChain" | "peerMeta";
|
|
60
60
|
type _GuardMailboxAppend = AssertAllKeysHandled<Exclude<keyof MailboxAppendMessage, MailboxAppendProjected>>;
|
|
61
61
|
type _GuardMailboxAppendReverse = AssertAllKeysHandled<Exclude<MailboxAppendProjected, keyof MailboxAppendMessage>>;
|
|
62
|
-
type WiringSectionProjected = "schemaVersion" | "leg" | "ask" | "question" | "interaction" | "elicit" | "parkLane" | "session" | "fleet" | "permissionRules" | "modelGate" | "autoMode" | "mcp" | "tools" | "hooks" | "lsp";
|
|
62
|
+
type WiringSectionProjected = "schemaVersion" | "leg" | "ask" | "question" | "interaction" | "elicit" | "parkLane" | "session" | "fleet" | "permissionRules" | "modelGate" | "autoMode" | "mcp" | "tools" | "hooks" | "lsp" | "writeProtection";
|
|
63
63
|
type WiringSectionExcluded = "governance" | "configFingerprint";
|
|
64
64
|
type _GuardWiringSections = AssertAllKeysHandled<Exclude<keyof WiringManifest, WiringSectionProjected | WiringSectionExcluded>>;
|
|
65
65
|
type _GuardWiringSectionsReverse = AssertAllKeysHandled<Exclude<WiringSectionProjected | WiringSectionExcluded, keyof WiringManifest>>;
|
|
@@ -100,6 +100,9 @@ type _GuardWiringMcpReverse = AssertAllKeysHandled<Exclude<WiringMcpHandled, key
|
|
|
100
100
|
type WiringRulesHandled = "storeWired" | "syncWired" | "orgGoverned";
|
|
101
101
|
type _GuardWiringRules = AssertAllKeysHandled<Exclude<keyof WiringSection<"permissionRules">, WiringRulesHandled>>;
|
|
102
102
|
type _GuardWiringRulesReverse = AssertAllKeysHandled<Exclude<WiringRulesHandled, keyof WiringSection<"permissionRules">>>;
|
|
103
|
+
type WiringWriteProtectionHandled = "targetView";
|
|
104
|
+
type _GuardWiringWriteProtection = AssertAllKeysHandled<Exclude<keyof WiringSection<"writeProtection">, WiringWriteProtectionHandled>>;
|
|
105
|
+
type _GuardWiringWriteProtectionReverse = AssertAllKeysHandled<Exclude<WiringWriteProtectionHandled, keyof WiringSection<"writeProtection">>>;
|
|
103
106
|
type WiringModelGateHandled = "class" | "removed" | "restore";
|
|
104
107
|
type _GuardWiringModelGate = AssertAllKeysHandled<Exclude<keyof WiringSection<"modelGate">, WiringModelGateHandled>>;
|
|
105
108
|
type _GuardWiringModelGateReverse = AssertAllKeysHandled<Exclude<WiringModelGateHandled, keyof WiringSection<"modelGate">>>;
|
|
@@ -180,6 +183,8 @@ export type CoreKeysetGuards = [
|
|
|
180
183
|
_GuardMailboxAppend,
|
|
181
184
|
_GuardMailboxAppendReverse,
|
|
182
185
|
_GuardWiringSections,
|
|
186
|
+
_GuardWiringWriteProtection,
|
|
187
|
+
_GuardWiringWriteProtectionReverse,
|
|
183
188
|
_GuardWiringSectionsReverse,
|
|
184
189
|
_GuardWiringLeg,
|
|
185
190
|
_GuardWiringLegReverse,
|
package/dist/trace/project.d.ts
CHANGED
|
@@ -584,6 +584,26 @@ export declare function workspaceChangedEventData(ev: {
|
|
|
584
584
|
* (「connection state is a fact about this leg's attempt, not about the assembly」)。
|
|
585
585
|
* ⚠️ 空数组 ≠ 缺席:core 顶注逐字「an empty array is "this leg declared no servers", absence is an older
|
|
586
586
|
* mint or an external derivation」⇒ 空数组照铸,别折成缺席。
|
|
587
|
+
* · `writeProtection`(`{ targetView: "spelling-only" | "target" }`;core 7.20.1 [ref] / clay [ref] 丙,
|
|
588
|
+
* S-361 提货件)—— **裁:租户可见,逐字透传**。它是 per-leg 的读面:「这条腿的写保护判官,拿到的是
|
|
589
|
+
* 哪一种目标读法」——`"target"` = 判官握着执行环境,拼法没命中时会在**解析后的真目标**上再判一次;
|
|
590
|
+
* `"spelling-only"` = 没有环境,拼法就是全部判决,只经别名/软链够到受保护行的写被清掉。
|
|
591
|
+
* ① **同主体同受众**:同一条腿、调用方自己的 run。而且这件事对该调用方**本就行为可观测** —— 在无手
|
|
592
|
+
* 车道上用一只声明了 `pathTarget` 写面的 MCP 工具对别名写一次就看得见(S-361 亲核可达)。剥掉它
|
|
593
|
+
* 并不收走任何能力,只收走那句诚实的标签,等于把 [ref] 刚刚消灭的**静默**在租户半场原样重建。
|
|
594
|
+
* ② **值的形**:core 的**两词闭集**,判据走 core 导出的 `WRITE_PROTECTION_TARGET_VIEWS` 常量(不抄
|
|
595
|
+
* 词表 —— 与 `delivered` 的 `MCP_DELIVERY_VERDICTS` 同一条);引擎派生、非用户内容 ⇒ 不脱敏、逐字。
|
|
596
|
+
* ③ 🔴 **最小披露仍然成立**:本键只答「判官有哪一种读法」,**从不**答「判官握着哪些路径」——
|
|
597
|
+
* 名表逐行内容(`rows` / `source` / `droppedDefaultRows`)照旧只在 operator 面
|
|
598
|
+
* (`GET /v1/diagnostics/wiring` 的 `writeProtection` 键,S-138),租户面照旧只有
|
|
599
|
+
* `/v1/capabilities.writeProtection.{armed, rows, replaced}` 三项计数。两者语义不同、同名不同物:
|
|
600
|
+
* 诊断面那一格是**本 server 装配的名表**(boot 产物),本段是**core 判官的自报读法**(per-leg)。
|
|
601
|
+
* ④ **不是 governance 侧信道**:它是「这条腿接没接手」的函数,与 lockedConfig/compliance/
|
|
602
|
+
* memoryAdmission/retention 四位无函数关系。⚠️ 但它**进** core 的 `configFingerprint`(core 顶注
|
|
603
|
+
* 逐字:which view the judge got is an ASSEMBLY fact)—— 指纹本就只发 operator 面,不受影响。
|
|
604
|
+
* ⚠️ **缺席 = 第三态**:core 的段是 **present-iff 编译出了判官**,`writeProtectedPaths: []` 的部署
|
|
605
|
+
* 整段缺席。缺席**绝不**折成 `"spelling-only"`(那是「判官在、但只认拼法」,完全不同的一件事),
|
|
606
|
+
* 也不铸 `writeProtection: {}`。词出闭集同样**不**折进缺席,走 `dropProjectedFace` 留痕(见那里)。
|
|
587
607
|
* 未来 core 加一段而它没有 audience 标签时,**先在这里补一条裁定再决定挑不挑键** —— 段级完备性钉
|
|
588
608
|
* (test/wiring-manifest-projection.test.ts)会在那一刻先把人拦下来 —— ⚠️ **但那条钉是夹具驱动的**:它只看
|
|
589
609
|
* `deriveWiringManifest(EFFECTIVE_FACTS)` 真铸出来的段,facts 不带的可选段(147③ 的 `autoMode` 就是)它一格
|
package/dist/trace/project.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { redactErrorMessage, redactModelText } from "../observability/run-terminal-log.js";
|
|
2
|
-
import { MCP_DELIVERY_VERDICTS, screenGateOutcome, ToolRoster, ToolRosterDelta } from "@sema-agent/core";
|
|
2
|
+
import { MCP_DELIVERY_VERDICTS, screenGateOutcome, ToolRoster, ToolRosterDelta, WRITE_PROTECTION_TARGET_VIEWS } from "@sema-agent/core";
|
|
3
3
|
import { Value } from "typebox/value";
|
|
4
4
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
5
5
|
import { redactSecrets, redactDeep } from "./redact.js";
|
|
@@ -481,6 +481,19 @@ export function wiringManifestEventData(ev) {
|
|
|
481
481
|
const lane = str(lspIn.lane);
|
|
482
482
|
return { mounted, ...(lane === undefined ? {} : { lane }) };
|
|
483
483
|
})();
|
|
484
|
+
const wpIn = sec(m.writeProtection);
|
|
485
|
+
const writeProtection = (() => {
|
|
486
|
+
if (m.writeProtection === undefined)
|
|
487
|
+
return undefined;
|
|
488
|
+
if (wpIn === undefined)
|
|
489
|
+
return dropProjectedFace("wiring_manifest.writeProtection is not an object");
|
|
490
|
+
const targetView = str(wpIn.targetView);
|
|
491
|
+
if (targetView === undefined)
|
|
492
|
+
return dropProjectedFace("wiring_manifest.writeProtection.targetView is not a non-empty string");
|
|
493
|
+
if (!WRITE_PROTECTION_TARGET_VIEWS.includes(targetView))
|
|
494
|
+
return dropProjectedFace("wiring_manifest.writeProtection.targetView is outside core WRITE_PROTECTION_TARGET_VIEWS");
|
|
495
|
+
return { targetView };
|
|
496
|
+
})();
|
|
484
497
|
const mcpIn = m.mcp;
|
|
485
498
|
const mcp = (() => {
|
|
486
499
|
if (!Array.isArray(mcpIn))
|
|
@@ -523,6 +536,7 @@ export function wiringManifestEventData(ev) {
|
|
|
523
536
|
...(mcp !== undefined ? { mcp } : {}),
|
|
524
537
|
...(hooks !== undefined ? { hooks } : {}),
|
|
525
538
|
...(lsp !== undefined ? { lsp } : {}),
|
|
539
|
+
...(writeProtection !== undefined ? { writeProtection } : {}),
|
|
526
540
|
...identityFields(ev),
|
|
527
541
|
};
|
|
528
542
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.80.1",
|
|
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.20.
|
|
57
|
+
"@sema-agent/core": "7.20.1",
|
|
58
58
|
"@sema-agent/settings-schema": "2.1.0",
|
|
59
59
|
"e2b": "^2.28.0",
|
|
60
60
|
"libsodium-wrappers": "^0.8.4",
|