@sema-agent/server 5.0.1 → 5.1.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.
|
@@ -77,7 +77,7 @@ export function createResolveSpec(ctx) {
|
|
|
77
77
|
// body.scenario is bounded to the allowlist — outside ⇒ typed 400 `scenario_not_allowed`). The ruling
|
|
78
78
|
// {scenario, allowlist} is center-RESOLVED and rides the caps view (TTL-cached, same fetch as runtimeCaps);
|
|
79
79
|
// no center / dry-run / anonymous / older center / caps blip ⇒ undefined ⇒ legacy body-or-default chain.
|
|
80
|
-
const requested = gateScenarioRequest(await principalCaps?.scenarioRuling(auth?.principal), body.scenario, config.defaultScenario);
|
|
80
|
+
const requested = gateScenarioRequest(await principalCaps?.scenarioRuling(auth?.principal), body.scenario, config.defaultScenario, Object.keys(scenarios));
|
|
81
81
|
// per-principal execution-lane policy — this worker's lane is a
|
|
82
82
|
// PROCESS identity (REMOTE_EXEC boot wiring, no per-task switch), so admission is the gate: lane ∉
|
|
83
83
|
// allowedLanes ⇒ typed 403 `execution_lane_not_allowed`. Same caps car as the scenario ruling (zero extra
|
package/dist/boot/stores.js
CHANGED
|
@@ -156,7 +156,11 @@ export async function openStores(ctx) {
|
|
|
156
156
|
rosterStore = new TiDBRosterStore(mysqlPool);
|
|
157
157
|
}
|
|
158
158
|
else if (backend?.kind === "local") {
|
|
159
|
-
|
|
159
|
+
// #109([2392] 提货):onCorruptRead 披露座接线——腐读响亮 warn([2195] 族:不静默折空)。
|
|
160
|
+
// 三挂点里 server 真用的只有这一个(session-policy=自家 SQL 双店;mailbox 用的 FileMailboxStore 无此座)。
|
|
161
|
+
rosterStore = new FileRosterStore(join(config.localDataRoot ?? localRoot, "roster.json"), {
|
|
162
|
+
onCorruptRead: (info) => logger.warn("roster_corrupt_read", { path: info.path, reason: info.reason }),
|
|
163
|
+
});
|
|
160
164
|
}
|
|
161
165
|
if (rosterStore)
|
|
162
166
|
logger.info("roster_store_enabled", { backend: pgPool ? "pg" : mysqlPool ? "tidb" : "file" });
|
|
@@ -196,5 +196,5 @@ export interface ScenarioRuling {
|
|
|
196
196
|
* 到现状,与 workflows 面的 fail-closed 是刻意的 posture 差异,见 runtime-caps-resolver)。
|
|
197
197
|
* - body.scenario 未给:ruling 胜者 ⇒ 指派为默认;无胜者 ⇒ defaultScenario 现状链。
|
|
198
198
|
*/
|
|
199
|
-
export declare function gateScenarioRequest(ruling: ScenarioRuling | undefined, bodyScenario: unknown, defaultScenario: string): string;
|
|
199
|
+
export declare function gateScenarioRequest(ruling: ScenarioRuling | undefined, bodyScenario: unknown, defaultScenario: string, known: readonly string[]): string;
|
|
200
200
|
//# sourceMappingURL=scenarios.d.ts.map
|
|
@@ -417,8 +417,18 @@ export function mergeUserSkills(scenarioSkills, userSkills, logger) {
|
|
|
417
417
|
* 到现状,与 workflows 面的 fail-closed 是刻意的 posture 差异,见 runtime-caps-resolver)。
|
|
418
418
|
* - body.scenario 未给:ruling 胜者 ⇒ 指派为默认;无胜者 ⇒ defaultScenario 现状链。
|
|
419
419
|
*/
|
|
420
|
-
export function gateScenarioRequest(ruling, bodyScenario, defaultScenario) {
|
|
420
|
+
export function gateScenarioRequest(ruling, bodyScenario, defaultScenario, known) {
|
|
421
421
|
if (typeof bodyScenario === "string") {
|
|
422
|
+
// [2395]D③(5.1.0):body 显式给出的**未知名**响亮 400——此前静默折 default(oa 退役后老调用方
|
|
423
|
+
// 无声换了场景=更宽工具面,与零兼容纲领相反)。词表面先于治理面:名字都不存在时谈不上 allowlist。
|
|
424
|
+
// 指派链(ruling/default)不在此验——悬空指派名仍走 resolveSpec 既有 unknown→default(runtime-caps
|
|
425
|
+
// 面的误配另案,不该让一次中心误配把每个请求 400 死)。
|
|
426
|
+
if (!known.includes(bodyScenario)) {
|
|
427
|
+
throw new HttpError(400, `unknown scenario "${bodyScenario}" — this deployment's scenarios are: ${known.join(", ")}`, {
|
|
428
|
+
code: "scenario_unknown",
|
|
429
|
+
extra: { known: [...known] },
|
|
430
|
+
});
|
|
431
|
+
}
|
|
422
432
|
const allowed = ruling?.allowlist ?? [];
|
|
423
433
|
if (allowed.length > 0 && !allowed.includes(bodyScenario)) {
|
|
424
434
|
throw new HttpError(400, `scenario "${bodyScenario}" is not in this principal's assigned allowlist`, {
|
|
@@ -123,7 +123,9 @@ export interface FleetTaskRow {
|
|
|
123
123
|
/** One workflow row (wire subset of contract `FleetWorkflow`). */
|
|
124
124
|
export interface FleetWorkflowRow {
|
|
125
125
|
id: string;
|
|
126
|
-
|
|
126
|
+
/** [2400] FLEET-WORKFLOWS-1([2070]③ 同款,workflow 行此前漏网):缺席=诚实缺席,**不铸 id 冒充名**。
|
|
127
|
+
* 现役唯一发布者(workflow-notify-journal)恒带 redact 后的真名;此可选位只为乱序/部分 delta 诚实。 */
|
|
128
|
+
name?: string;
|
|
127
129
|
description?: string;
|
|
128
130
|
/** Owning principal (= the workflow run's scope). Same owner-gate as FleetTaskRow. NOT rendered. */
|
|
129
131
|
scope?: string;
|
package/dist/fleet/fleet-bus.js
CHANGED
|
@@ -92,7 +92,7 @@ export class FleetEventBus {
|
|
|
92
92
|
// └────────────────────────────────────────────────────────────────────────────────────────────────┘
|
|
93
93
|
/** Upsert a workflow row (MERGE by `id`) + fan out. */
|
|
94
94
|
publishWorkflow(delta) {
|
|
95
|
-
const merged = { ...(this.workflows.get(delta.id) ?? { id: delta.id,
|
|
95
|
+
const merged = { ...(this.workflows.get(delta.id) ?? { id: delta.id, status: "running" }), ...delta };
|
|
96
96
|
this.workflows.set(delta.id, merged);
|
|
97
97
|
this.emit({ type: "workflow", row: merged, ts: this.now() });
|
|
98
98
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.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",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"sharp": "^0.35.3"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
|
-
"@sema-agent/sdk": "^4.
|
|
71
|
+
"@sema-agent/sdk": "^4.1.0",
|
|
72
72
|
"@types/libsodium-wrappers": "^0.7.14",
|
|
73
73
|
"@types/node": "22.10.2",
|
|
74
74
|
"@types/pg": "^8.20.0",
|