@sema-agent/server 7.97.0 → 7.99.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/MIGRATION.md +11 -0
- package/README.md +3 -3
- package/README.zh-CN.md +3 -3
- package/USAGE.md +18 -9
- package/deploy/sema-up/cluster-up.sh +8 -4
- package/deploy/sema-up/kube-up.sh +40 -15
- package/deploy/sema-up/lib/envf.sh +241 -0
- package/deploy/sema-up/sema-up.sh +162 -101
- package/deploy/sema-up/smoke.sh +8 -1
- package/dist/boot/config-center.js +169 -24
- package/dist/boot/org-memory.d.ts +2 -1
- package/dist/boot/org-memory.js +9 -5
- package/dist/boot/resolve-spec.js +9 -4
- package/dist/boot/runtime-caps.js +4 -4
- package/dist/brain.js +28 -18
- package/dist/capabilities/center-prompts.d.ts +3 -0
- package/dist/capabilities/center-prompts.js +1 -1
- package/dist/center-credential.d.ts +112 -0
- package/dist/center-credential.js +131 -0
- package/dist/config-catalog.js +7 -6
- package/dist/config-center/apply-effective.js +7 -1
- package/dist/config-center/center-request.d.ts +60 -0
- package/dist/config-center/center-request.js +71 -0
- package/dist/config-center/facade.d.ts +2 -1
- package/dist/config-center/http-client.d.ts +17 -9
- package/dist/config-center/http-client.js +27 -36
- package/dist/config-center/skills-mcp.d.ts +4 -1
- package/dist/config-center/skills-mcp.js +2 -2
- package/dist/config-invariants.d.ts +2 -2
- package/dist/config-invariants.js +9 -9
- package/dist/config-lkg.d.ts +47 -8
- package/dist/config-lkg.js +107 -20
- package/dist/config-provider.d.ts +8 -7
- package/dist/config-provider.js +5 -5
- package/dist/config-types.d.ts +20 -6
- package/dist/config.d.ts +9 -0
- package/dist/config.js +53 -18
- package/dist/fleet-client.d.ts +8 -8
- package/dist/fleet-client.js +7 -7
- package/dist/fleet-lease.d.ts +11 -11
- package/dist/fleet-lease.js +17 -13
- package/dist/hooks/hook-llm.d.ts +1 -1
- package/dist/hooks/hook-llm.js +3 -1
- package/dist/hosted-posture.d.ts +49 -93
- package/dist/hosted-posture.js +1 -25
- package/dist/memory-scope.d.ts +62 -11
- package/dist/memory-scope.js +32 -3
- package/dist/model-provider.d.ts +13 -0
- package/dist/model-provider.js +3 -0
- package/dist/model-route-endpoint.d.ts +82 -0
- package/dist/model-route-endpoint.js +55 -0
- package/dist/observability/secret-env-scrub.d.ts +2 -26
- package/dist/observability/secret-env-scrub.js +2 -2
- package/dist/plugins/remote-env-host.d.ts +3 -1
- package/dist/plugins/remote-env-host.js +3 -2
- package/dist/project-identity.d.ts +29 -0
- package/dist/project-identity.js +120 -0
- package/dist/project-memory.d.ts +28 -1
- package/dist/project-memory.js +22 -22
- package/dist/run-local.d.ts +14 -4
- package/dist/run-local.js +8 -7
- package/dist/runtime-caps-resolver.d.ts +8 -8
- package/dist/runtime-caps-resolver.js +16 -14
- package/dist/sealed-key.js +3 -7
- package/dist/server-secret-env.d.ts +41 -0
- package/dist/server-secret-env.js +18 -0
- package/dist/tool-approval.d.ts +48 -1
- package/dist/tool-approval.js +10 -3
- package/dist/trace/redact.d.ts +1 -1
- package/package.json +4 -3
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/** 凭证种类闭集。新增一个种类 = 本文件与 `config-center/center-request.ts` 的穷举 switch 当场编译红。 */
|
|
2
|
+
export type CenterCredentialKind = "pull-token" | "user-jwt" | "none";
|
|
3
|
+
/** 凭证在哪里声明(env 形带值 —— 只在本模块内流转)。 */
|
|
4
|
+
export type CenterCredentialDeclaration = {
|
|
5
|
+
readonly from: "env";
|
|
6
|
+
readonly token: string;
|
|
7
|
+
} | {
|
|
8
|
+
readonly from: "file";
|
|
9
|
+
readonly path: string;
|
|
10
|
+
} | {
|
|
11
|
+
readonly from: "absent";
|
|
12
|
+
};
|
|
13
|
+
/** 声明形的**非秘密**投影(进日志 / 拒因 / 未来的 caps 读面):env 形不带值。 */
|
|
14
|
+
export type CenterCredentialOrigin = {
|
|
15
|
+
readonly from: "env";
|
|
16
|
+
} | {
|
|
17
|
+
readonly from: "file";
|
|
18
|
+
readonly path: string;
|
|
19
|
+
} | {
|
|
20
|
+
readonly from: "absent";
|
|
21
|
+
};
|
|
22
|
+
/** 一次取值的结果。`ok:false` 只有文件形会出现(env / absent 形的取值恒成功)。 */
|
|
23
|
+
export type CenterCredentialRead = {
|
|
24
|
+
readonly ok: true;
|
|
25
|
+
readonly kind: "pull-token";
|
|
26
|
+
readonly bearer: string;
|
|
27
|
+
readonly identity: string;
|
|
28
|
+
readonly workerScoped: boolean;
|
|
29
|
+
} | {
|
|
30
|
+
readonly ok: true;
|
|
31
|
+
readonly kind: "user-jwt";
|
|
32
|
+
readonly bearer: string;
|
|
33
|
+
readonly identity: string;
|
|
34
|
+
} | {
|
|
35
|
+
readonly ok: true;
|
|
36
|
+
readonly kind: "none";
|
|
37
|
+
readonly identity: string;
|
|
38
|
+
} | {
|
|
39
|
+
readonly ok: false;
|
|
40
|
+
readonly reason: string;
|
|
41
|
+
};
|
|
42
|
+
/** 健康态:最近一次取值的结果(caps `leaseHealth` 之类的读面读它,不自己再判)。 */
|
|
43
|
+
export type CenterCredentialHealth = {
|
|
44
|
+
readonly state: "ok";
|
|
45
|
+
} | {
|
|
46
|
+
readonly state: "unreadable";
|
|
47
|
+
readonly reason: string;
|
|
48
|
+
readonly since: number;
|
|
49
|
+
readonly failures: number;
|
|
50
|
+
};
|
|
51
|
+
export interface CenterCredentialSource {
|
|
52
|
+
/** 声明形(非秘密):`{from:"file", path}` 进日志与拒因用。 */
|
|
53
|
+
readonly origin: CenterCredentialOrigin;
|
|
54
|
+
/** 装配判据:声明了凭证(env 值非空 / 文件路径在场)。**按声明装,不按内容装** —— 文件此刻不可读也算声明了。 */
|
|
55
|
+
readonly declared: boolean;
|
|
56
|
+
/** 每次出站请求调用一次;文件形每次都读文件。 */
|
|
57
|
+
read(): CenterCredentialRead;
|
|
58
|
+
health(): CenterCredentialHealth;
|
|
59
|
+
}
|
|
60
|
+
/** 凭证文件此刻不可读 / 为空。中心请求装配点(`center-request.ts`)取值失败时抛它,各消费方按自己的
|
|
61
|
+
* 失败臂处置(拉配置保 LKG、caps fail-closed、租约走「鉴权失败」响亮臂 …),**不发一个空 bearer 出去**。 */
|
|
62
|
+
export declare class CenterCredentialUnavailableError extends Error {
|
|
63
|
+
readonly reason: string;
|
|
64
|
+
constructor(reason: string);
|
|
65
|
+
}
|
|
66
|
+
export declare const CENTER_TOKEN_ENV = "SEMA_REGISTRY_TOKEN";
|
|
67
|
+
export declare const CENTER_TOKEN_FILE_ENV = "SEMA_REGISTRY_TOKEN_FILE";
|
|
68
|
+
/**
|
|
69
|
+
* 读两个声明键。两个都非空 ⇒ throw(boot 拒启,拒因点名二选一)。空串 = 未设(与 7.98.0 之前
|
|
70
|
+
* `SEMA_REGISTRY_TOKEN || ""` 的读法一致)。文件路径在这里解析成绝对路径(之后 cwd 变了也读同一个文件)。
|
|
71
|
+
*/
|
|
72
|
+
export declare function readCenterCredentialDeclaration(env?: NodeJS.ProcessEnv): CenterCredentialDeclaration;
|
|
73
|
+
/** 拉取令牌形的身份 = worker(拉取令牌代表 worker,不是人)。种类判别与 formatVersion 1 LKG 的一次性迁移
|
|
74
|
+
* (`config-lkg.ts` `migrateV1Lkg`:v1 信封只记 worker,只有这一形的身份推得出来)共用这一只。 */
|
|
75
|
+
export declare function pullTokenIdentity(worker: string | undefined): string;
|
|
76
|
+
/**
|
|
77
|
+
* 凭证来源工厂(活对象:文件形每次取值读盘并记健康态)。`worker` = `SEMA_REGISTRY_WORKER`(拉取令牌形的身份)。
|
|
78
|
+
* `readFile` 只为测试注入。
|
|
79
|
+
*/
|
|
80
|
+
export declare function createCenterCredentialSource(declaration: CenterCredentialDeclaration, worker: string | undefined, deps?: {
|
|
81
|
+
readFile?: (path: string) => string;
|
|
82
|
+
now?: () => number;
|
|
83
|
+
}): CenterCredentialSource;
|
|
84
|
+
/** 需要作用域 worker 的拉取令牌(`wpt_<self>`)却没配 `SEMA_REGISTRY_WORKER` ⇒ 中心对 per-principal caps
|
|
85
|
+
* 逐次 403(caps fail-closed 拒全部 workflow)。boot 期一次性诊断用;判据只读本模块的种类判别。 */
|
|
86
|
+
export declare function scopedTokenNeedsWorker(read: CenterCredentialRead, worker: string | undefined): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* [ref] E-15 —— 按「worker + 凭证身份」分箱的**唯一键形**(LKG 文件名、prompt 活动状态目录共用)。
|
|
89
|
+
* 两段都取摘要:worker 名 / 身份任意字节都成单段路径,无 dot-segment,无撞形(codex-5 当年给 prompt 状态
|
|
90
|
+
* 目录定的姿势,本批推广到 LKG)。
|
|
91
|
+
*/
|
|
92
|
+
export declare function centerStateBin(worker: string | undefined, identity: string): string;
|
|
93
|
+
/** 此刻凭证的身份(每次现读);文件形此刻不可读 ⇒ `undefined`(身份未知)。「我现在是谁」只有这一只读法。 */
|
|
94
|
+
export declare function credentialIdentityNow(source: CenterCredentialSource): string | undefined;
|
|
95
|
+
/**
|
|
96
|
+
* [ref] E-15 ② —— 一次中心往返的答复**仍属于此刻的凭证**:发出它所用的身份 = 此刻的身份。
|
|
97
|
+
* 拉配置(`boot/config-center.ts` `pullEffective`)与两条 `?principal=` 视图(`config-center/http-client.ts`)
|
|
98
|
+
* 都只问这一只;不成立 ⇒ 答复丢弃,当这次拉取失败(各走各的失败臂)。
|
|
99
|
+
*/
|
|
100
|
+
export declare function answerStillCurrent(source: CenterCredentialSource, usedIdentity: string | undefined): usedIdentity is string;
|
|
101
|
+
/**
|
|
102
|
+
* [ref] E-15 · 内存面 —— 身份绑定的进程内状态(per-principal caps 缓存与在飞表、org 记忆目录)的**唯一分箱规则**,
|
|
103
|
+
* 与盘上的 {@link centerStateBin} 同一条规则的两面(7.99.0 合并复审 F2:此前两处缓存只按 principal 键)。
|
|
104
|
+
* · 箱 = 取箱那一刻的凭证身份(惰建)。凭证此刻不可读 ⇒ 「身份未知」箱。
|
|
105
|
+
* · 每个箱只经**钉在它身份上**的凭证来源对中心说话(`make` 拿到的那只):此刻的凭证已换了人 / 不可读 ⇒ 这一箱的
|
|
106
|
+
* 请求不发(取值失败 ⇒ `CenterCredentialUnavailableError`,消费方走自己的失败臂)。身份未知箱因此从不收任何
|
|
107
|
+
* 中心答复;缓存 / 在飞 / 条件请求的校验子全在箱里,换了人结构上够不着上一个人的。
|
|
108
|
+
* · 请求发出之后才换人:答复到达时由 {@link answerStillCurrent} 判丢弃(请求装配侧,见 http-client.ts)。
|
|
109
|
+
* 旧身份的箱不淘汰(量级 = 进程生命期内出现过的身份数):换回来的身份在 TTL 内照常命中它自己的缓存。
|
|
110
|
+
*/
|
|
111
|
+
export declare function createIdentityBins<T>(source: CenterCredentialSource, make: (pinned: CenterCredentialSource) => T): () => T;
|
|
112
|
+
//# sourceMappingURL=center-credential.d.ts.map
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { resolve as resolvePath } from "node:path";
|
|
4
|
+
import { looksLikeJwt } from "@sema-agent/settings-schema/api/auth-bridge";
|
|
5
|
+
export class CenterCredentialUnavailableError extends Error {
|
|
6
|
+
reason;
|
|
7
|
+
constructor(reason) {
|
|
8
|
+
super(`center credential unavailable: ${reason}`);
|
|
9
|
+
this.reason = reason;
|
|
10
|
+
this.name = "CenterCredentialUnavailableError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export const CENTER_TOKEN_ENV = "SEMA_REGISTRY_TOKEN";
|
|
14
|
+
export const CENTER_TOKEN_FILE_ENV = "SEMA_REGISTRY_TOKEN_FILE";
|
|
15
|
+
export function readCenterCredentialDeclaration(env = process.env) {
|
|
16
|
+
const token = env.SEMA_REGISTRY_TOKEN ?? "";
|
|
17
|
+
const file = env.SEMA_REGISTRY_TOKEN_FILE ?? "";
|
|
18
|
+
if (token !== "" && file !== "") {
|
|
19
|
+
throw new Error(`${CENTER_TOKEN_ENV} and ${CENTER_TOKEN_FILE_ENV} are both set — pick exactly one: ${CENTER_TOKEN_ENV} carries the credential value, ` +
|
|
20
|
+
`${CENTER_TOKEN_FILE_ENV} names a file re-read on every request (rotation without restart). Two writers on one credential is a guess, not a config.`);
|
|
21
|
+
}
|
|
22
|
+
if (file !== "")
|
|
23
|
+
return { from: "file", path: resolvePath(file) };
|
|
24
|
+
if (token !== "")
|
|
25
|
+
return { from: "env", token };
|
|
26
|
+
return { from: "absent" };
|
|
27
|
+
}
|
|
28
|
+
const digest16 = (s) => createHash("sha256").update(s, "utf8").digest("hex").slice(0, 16);
|
|
29
|
+
function jwtSubject(bearer) {
|
|
30
|
+
let parsed;
|
|
31
|
+
try {
|
|
32
|
+
parsed = JSON.parse(Buffer.from(bearer.split(".")[1] ?? "", "base64url").toString("utf8"));
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
return { missing: `payload is not JSON (${err instanceof Error ? err.name : "parse error"})` };
|
|
36
|
+
}
|
|
37
|
+
if (parsed === null || typeof parsed !== "object" || !("sub" in parsed))
|
|
38
|
+
return { missing: "no sub claim" };
|
|
39
|
+
return typeof parsed.sub === "string" && parsed.sub !== "" ? { sub: parsed.sub } : { missing: "sub is not a non-empty string" };
|
|
40
|
+
}
|
|
41
|
+
function classify(bearer, worker) {
|
|
42
|
+
if (looksLikeJwt(bearer)) {
|
|
43
|
+
const s = jwtSubject(bearer);
|
|
44
|
+
return { ok: true, kind: "user-jwt", bearer, identity: "sub" in s ? `user-jwt:${digest16(s.sub)}` : `user-jwt-opaque:${digest16(bearer)}` };
|
|
45
|
+
}
|
|
46
|
+
return { ok: true, kind: "pull-token", bearer, identity: pullTokenIdentity(worker), workerScoped: bearer.startsWith("wpt_") };
|
|
47
|
+
}
|
|
48
|
+
export function pullTokenIdentity(worker) {
|
|
49
|
+
return `pull-token:${worker ?? "global"}`;
|
|
50
|
+
}
|
|
51
|
+
const NONE = { ok: true, kind: "none", identity: "none" };
|
|
52
|
+
export function createCenterCredentialSource(declaration, worker, deps = {}) {
|
|
53
|
+
switch (declaration.from) {
|
|
54
|
+
case "absent":
|
|
55
|
+
return { origin: declaration, declared: false, read: () => NONE, health: () => ({ state: "ok" }) };
|
|
56
|
+
case "env": {
|
|
57
|
+
const fixed = classify(declaration.token, worker);
|
|
58
|
+
return { origin: { from: "env" }, declared: true, read: () => fixed, health: () => ({ state: "ok" }) };
|
|
59
|
+
}
|
|
60
|
+
case "file": {
|
|
61
|
+
const readFile = deps.readFile ?? ((p) => readFileSync(p, "utf8"));
|
|
62
|
+
const now = deps.now ?? Date.now;
|
|
63
|
+
let health = { state: "ok" };
|
|
64
|
+
const fail = (reason) => {
|
|
65
|
+
health = health.state === "unreadable" ? { ...health, reason, failures: health.failures + 1 } : { state: "unreadable", reason, since: now(), failures: 1 };
|
|
66
|
+
return { ok: false, reason };
|
|
67
|
+
};
|
|
68
|
+
return {
|
|
69
|
+
origin: declaration,
|
|
70
|
+
declared: true,
|
|
71
|
+
read() {
|
|
72
|
+
let text;
|
|
73
|
+
try {
|
|
74
|
+
text = readFile(declaration.path);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
const code = err instanceof Error && "code" in err && typeof err.code === "string" ? err.code : "read error";
|
|
78
|
+
return fail(`${CENTER_TOKEN_FILE_ENV} ${declaration.path} is unreadable (${code})`);
|
|
79
|
+
}
|
|
80
|
+
const bearer = text.trim();
|
|
81
|
+
if (bearer === "")
|
|
82
|
+
return fail(`${CENTER_TOKEN_FILE_ENV} ${declaration.path} is empty`);
|
|
83
|
+
health = { state: "ok" };
|
|
84
|
+
return classify(bearer, worker);
|
|
85
|
+
},
|
|
86
|
+
health: () => health,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export function scopedTokenNeedsWorker(read, worker) {
|
|
92
|
+
return read.ok && read.kind === "pull-token" && read.workerScoped && !worker;
|
|
93
|
+
}
|
|
94
|
+
export function centerStateBin(worker, identity) {
|
|
95
|
+
return `${worker === undefined ? "global" : `worker-${digest16(worker)}`}.id-${digest16(identity)}`;
|
|
96
|
+
}
|
|
97
|
+
export function credentialIdentityNow(source) {
|
|
98
|
+
const r = source.read();
|
|
99
|
+
return r.ok ? r.identity : undefined;
|
|
100
|
+
}
|
|
101
|
+
export function answerStillCurrent(source, usedIdentity) {
|
|
102
|
+
return usedIdentity !== undefined && credentialIdentityNow(source) === usedIdentity;
|
|
103
|
+
}
|
|
104
|
+
export function createIdentityBins(source, make) {
|
|
105
|
+
const bins = new Map();
|
|
106
|
+
return () => {
|
|
107
|
+
const identity = credentialIdentityNow(source);
|
|
108
|
+
if (bins.has(identity))
|
|
109
|
+
return bins.get(identity);
|
|
110
|
+
const made = make(pinCredential(source, identity));
|
|
111
|
+
bins.set(identity, made);
|
|
112
|
+
return made;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function pinCredential(source, identity) {
|
|
116
|
+
return {
|
|
117
|
+
origin: source.origin,
|
|
118
|
+
declared: source.declared,
|
|
119
|
+
read() {
|
|
120
|
+
const r = source.read();
|
|
121
|
+
if (!r.ok)
|
|
122
|
+
return r;
|
|
123
|
+
if (identity === undefined) {
|
|
124
|
+
return { ok: false, reason: "this state bin was opened while the center credential was unreadable (identity unknown) — it never talks to the center; the next call runs in the current identity's bin" };
|
|
125
|
+
}
|
|
126
|
+
return r.identity === identity ? r : { ok: false, reason: "center credential identity changed — this request belongs to the previous identity's state bin and is not sent" };
|
|
127
|
+
},
|
|
128
|
+
health: () => source.health(),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=center-credential.js.map
|
package/dist/config-catalog.js
CHANGED
|
@@ -74,7 +74,7 @@ export const CONFIG_CATALOG = [
|
|
|
74
74
|
r("ATTACHMENT_ORPHAN_GRACE_MS", "limitsHttp", "number", "附件孤儿对象 GC 宽限(ms;0=关本腿,坏值回默认+warn)", { staticDefault: "3600000", danger: DATA, center: cLimits("attachmentOrphanGraceMs") }),
|
|
75
75
|
r("ATTACHMENT_UNBOUND_TTL_MS", "limitsHttp", "number", "未绑定附件的 TTL(ms)", { staticDefault: String(24 * 3600 * 1000), danger: DATA, configKey: "attachmentUnboundTtlMs", center: cLimits("attachmentUnboundTtlMs") }),
|
|
76
76
|
r("AUTH_BRIDGE_AUDIENCE", "auth", "string", "registry SSO-JWT 验签期望 aud", { danger: SEC }),
|
|
77
|
-
r("AUTH_BRIDGE_ISSUER", "auth", "url", "registry SSO-JWT issuer(auth bridge 的开关之一;S-
|
|
77
|
+
r("AUTH_BRIDGE_ISSUER", "auth", "url", "registry SSO-JWT issuer(auth bridge 的开关之一;S-671 / C-R100 ① 起**不是**托管证据 —— 多租户只认 REQUIRE_PRINCIPAL=true)", { danger: SEC, derivedDefaultNote: "缺席 ⇒ 桥的装配处回退到 configCenter.baseUrl(配了 center 即默认开桥);显式值与回退值在托管判定上同判(都不算)", configKey: "authBridgeIssuer" }),
|
|
78
78
|
r("AUTH_BRIDGE_JWKS_URL", "auth", "url", "registry SSO-JWT 的 JWKS 地址(缺省从 issuer 派生)", { danger: SEC }),
|
|
79
79
|
r("AUTONOMY", "auth", "enum", "运维 autonomy 模式,tighten-only 叠到每个 TaskSpec(read-only/plan ⇒ 只读手,ask ⇒ 恒 shellGate);center 腿热且 non-sticky", { danger: SEC, enumValues: AUTONOMY_MODES, configKey: "autonomy", center: cGov("autonomy") }),
|
|
80
80
|
r("AUTO_MODE_CLASSIFIER_FALLBACK", "approval", "enum", "auto 分类器席答不出裁决时的梯子(main=主模型代判 / none=只留分类器那一级,整条拒)", { enumValues: ["main", "none"], danger: SEC, derivedDefaultNote: "未设/空串 ⇒ 不铸键,跟随引擎缺省 main(core 7.18.0 起;空串按未设,与其余词表旋钮同判);none = 7.77.0 及以前的行为逐字;词表外的值启动期拒启。权限轴放宽,升级须知见 DEPLOY-PREREQS", configKey: "autoModeClassifierFallback" }),
|
|
@@ -245,14 +245,14 @@ export const CONFIG_CATALOG = [
|
|
|
245
245
|
r("MEMORY_SYNC_MAX_PUSH_ENTRIES", "memory", "number", "TOC 同步单轮 push 条数帽(设了必须 ≥1 整数)", { derivedDefaultNote: "缺席 ⇒ 键缺席,core 默认" }),
|
|
246
246
|
r("MEMORY_SYNC_SCOPE", "memory", "string", "TOC 同步 scope 覆写", { derivedDefaultNote: "缺席 ⇒ 从 MEMORY_SCOPE 铸 user 盘键" }),
|
|
247
247
|
r("MEMORY_SYNC_TOKEN", "memory", "secret", "TOC 同步中心面的 Bearer(URL 设了它必须在,半配拒启)", { danger: SEC }),
|
|
248
|
-
r("MEMORY_SYNC_URL", "memory", "url", "TOC 同步中心地址(设了=开;file 记忆形专用,DB
|
|
248
|
+
r("MEMORY_SYNC_URL", "memory", "url", "TOC 同步中心地址(设了=开;file 记忆形专用,DB 后端配了拒启;同步只覆盖一层 ⇒ 单用户任务不按项目键控、写家落全局层 MEMORY_SCOPE(S-512 让位,启动通知 `memory_project_scope_yields_to_sync`))"),
|
|
249
249
|
r("METRICS_TOKEN", "observability", "secret", "/metrics 只读令牌", { danger: SEC }),
|
|
250
250
|
r("MINIO_ACCESS_KEY", "store", "secret", "对象存储 access key(快照 blob/leader 工作区/公链签发共用)", { danger: SEC }),
|
|
251
251
|
r("MINIO_BUCKET", "store", "string", "leader 工作区快照桶", { derivedDefaultNote: "缺席 ⇒ workspaces" }),
|
|
252
252
|
r("MINIO_ENDPOINT", "store", "url", "对象存储 endpoint(三键齐 ⇒ 快照 blob 走 MinIO,否则 SQL 行)", { danger: DATA }),
|
|
253
253
|
r("MINIO_REGION", "store", "string", "对象存储 region"),
|
|
254
254
|
r("MINIO_SECRET_KEY", "store", "secret", "对象存储 secret key", { danger: SEC }),
|
|
255
|
-
r("MODEL_API_KEY", "modelPlane", "secret", "网关腿共享 API key(per-model 覆写在 center
|
|
255
|
+
r("MODEL_API_KEY", "modelPlane", "secret", "网关腿共享 API key(per-model 覆写在 center 侧;S-623:只与 MODEL_GATEWAY_BASEURL 配对 —— 网关未设时不装载、不发往任何路由,启动通知 `model_gateway_key_unpaired`)", { danger: [...SEC, ...BILL] }),
|
|
256
256
|
r("MODEL_AUTO_COMPACT_TOKENS", "modelPlane", "number", "主槽 dual-window 触发阈(1M 窗不再隐式 967000,缺席则用普通触发几何)", { derivedDefaultNote: "缺席 ⇒ 不铸键(core W-33000 几何)" }),
|
|
257
257
|
r("MODEL_CASCADE_LADDER", "modelPlane", "csv", "质量门级联梯(目录名 cheapest→strongest;opt-in per-task cascade:true)", { danger: BILL, configKey: "cascadeLadder" }),
|
|
258
258
|
r("MODEL_CB_COOLDOWN_MS", "modelPlane", "number", "熔断冷却窗(ms)", { staticDefault: String(RESILIENCE_DEFAULTS.cooldownMs.value) }),
|
|
@@ -286,8 +286,8 @@ export const CONFIG_CATALOG = [
|
|
|
286
286
|
r("MODEL_EXTRA_BODY", "modelPlane", "opaque", "生成参数直通 JSON(top_k/logit_bias 等;必须静态 —— 前缀缓存键序)", { danger: SEC }),
|
|
287
287
|
r("MODEL_FIRST_TOKEN_TIMEOUT_MS", "modelPlane", "number", "首 token 超时(ms;0=关 —— 开流不吐字的唯一看门狗;自托管长 prefill 后端按此档留足)", { staticDefault: String(RESILIENCE_DEFAULTS.firstTokenTimeoutMs.value) }),
|
|
288
288
|
r("MODEL_FREQUENCY_PENALTY", "modelPlane", "number", "frequency_penalty 直通(压过 EXTRA_BODY 同键)"),
|
|
289
|
-
r("MODEL_GATEWAY_BASEURL", "modelPlane", "url", "openai 兼容网关 base URL(
|
|
290
|
-
r("MODEL_GATEWAY_FALLBACK_URLS", "modelPlane", "url", "网关故障转移地址表(CSV;S-475:**逐项**判 userinfo,任一项带 ⇒ 拒启,机读码 `config.gateway_url_userinfo`;S-483:逐项判可解析性,机读码 `config.model_route_url_invalid`;S-514:**逐项**判 `#fragment`,机读码 `config.model_route_url_fragment`)", { configKey: "gatewayFallbackUrls" }),
|
|
289
|
+
r("MODEL_GATEWAY_BASEURL", "modelPlane", "url", "openai 兼容网关 base URL(S-623:**无出厂缺省** —— 配置能派发到的每条 openai 兼容路由都必须解析到显式端点(条目自己的 baseUrl,或本键),否则拒启 / 热路径整批拒,机读码 `config.model_route_endpoint_missing`,拒因点名条目并给出本机写法 `MODEL_GATEWAY_BASEURL=<本机网关>/v1`;anthropic 单路由部署不设本键合法;S-475:带 userinfo 拒启,机读码 `config.gateway_url_userinfo` —— WHATWG 禁止用带凭据的 URL 构造 fetch Request,凭据放 MODEL_API_KEY;S-483:解析不动/不透明形拒启,机读码 `config.model_route_url_invalid`;S-514:带 `#fragment` 拒启,机读码 `config.model_route_url_fragment`)", { derivedDefaultNote: "缺席 ⇒ 本部署没有网关(不铸键);有 openai 路由落不到端点 ⇒ 拒启", configKey: "gatewayBaseUrl", center: cModels("models[].baseUrl") }),
|
|
290
|
+
r("MODEL_GATEWAY_FALLBACK_URLS", "modelPlane", "url", "网关故障转移地址表(CSV;S-623:主网关 MODEL_GATEWAY_BASEURL 未设而本键设了 ⇒ 拒启,机读码 `config.gateway_fallback_without_primary` —— 备用是主网关的镜像;S-475:**逐项**判 userinfo,任一项带 ⇒ 拒启,机读码 `config.gateway_url_userinfo`;S-483:逐项判可解析性,机读码 `config.model_route_url_invalid`;S-514:**逐项**判 `#fragment`,机读码 `config.model_route_url_fragment`)", { configKey: "gatewayFallbackUrls" }),
|
|
291
291
|
r("MODEL_ID", "modelPlane", "string", "主模型 id(3.0.0 起无出厂缺省,缺 ⇒ 拒启指路)", { configKey: "model", resolve: (c) => c.model.id, center: cModels("models[].id") }),
|
|
292
292
|
r("MODEL_IDLE_TIMEOUT_MS", "modelPlane", "number", "流中静默看门狗(ms;0=关)", { staticDefault: String(RESILIENCE_DEFAULTS.idleTimeoutMs.value) }),
|
|
293
293
|
r("MODEL_MAX_TOKENS", "modelPlane", "number", "主模型输出上限", { derivedDefaultNote: "缺席 ⇒ min(32000, contextWindow)", resolve: (c) => c.model.maxTokens ?? null, center: cModels("models[].maxTokens") }),
|
|
@@ -394,7 +394,8 @@ export const CONFIG_CATALOG = [
|
|
|
394
394
|
r("SEMA_PIP_INDEX", "orchestration", "url", "SANDBOX_PKG_SOURCE=custom 的显式 pip index 地址"),
|
|
395
395
|
r("SEMA_REGISTRY_ADMIN_TOKEN", "integrations", "secret", "写 center 的管理凭证(仅 boot 上报执行公钥用;刻意不入 ServiceConfig —— 不进 configHash/日志面)", { danger: SEC, scope: "bootstrap-only" }),
|
|
396
396
|
r("SEMA_REGISTRY_DRY_RUN", "integrations", "boolean", "center 腿 dry-run(拉而不应用)", { staticDefault: "false" }),
|
|
397
|
-
r("SEMA_REGISTRY_TOKEN", "integrations", "secret", "center 拉取凭据", { danger: SEC }),
|
|
397
|
+
r("SEMA_REGISTRY_TOKEN", "integrations", "secret", "center 拉取凭据(值形;与 SEMA_REGISTRY_TOKEN_FILE 二选一,都设=拒启)", { danger: SEC }),
|
|
398
|
+
r("SEMA_REGISTRY_TOKEN_FILE", "integrations", "path", "center 拉取凭据文件(每条中心请求都重读 ⇒ 轮换 / 换账号不重启;与 SEMA_REGISTRY_TOKEN 二选一,都设=拒启;文件不可读=响亮 + 健康态 unreadable,不当作无凭证)", { danger: SEC }),
|
|
398
399
|
r("SEMA_REGISTRY_URL", "integrations", "url", "sema-registry 地址(设了=remote 配置源开关)"),
|
|
399
400
|
r("SEMA_REGISTRY_WORKER", "integrations", "string", "本 worker 在 center 的名字(per-worker effective)"),
|
|
400
401
|
r("SEMA_UV_INDEX", "orchestration", "url", "SANDBOX_PKG_SOURCE=custom 的显式 uv index 地址"),
|
|
@@ -7,6 +7,7 @@ import { projectCollabToWorkflows, registerCollabWorkflows } from "../capabiliti
|
|
|
7
7
|
import { stageLimits } from "./stage-limits.js";
|
|
8
8
|
import { BATCH1_LIMIT_KEYS } from "./hot-keys-registry.js";
|
|
9
9
|
import { buildDisplaySafeUrl } from "../trace/redact.js";
|
|
10
|
+
import { crossInvariant } from "../config-invariants.js";
|
|
10
11
|
const charsPerTokenOf = (v) => (typeof v === "number" && Number.isFinite(v) && v > 0 ? v : undefined);
|
|
11
12
|
function envDerivedModel(config) {
|
|
12
13
|
const m = config.envModel;
|
|
@@ -203,7 +204,7 @@ function stageEffective(config, eff, logger, opts) {
|
|
|
203
204
|
if (ok)
|
|
204
205
|
logger?.warn("sema_registry_model_base_url", { model: m.name, baseUrl: buildDisplaySafeUrl(rawBaseUrl), hint: "registry-directed gateway override — this model's key authenticates AGAINST THIS URL" });
|
|
205
206
|
else {
|
|
206
|
-
logger?.warn("sema_registry_model_base_url_rejected", { model: m.name, baseUrl: buildDisplaySafeUrl(String(rawBaseUrl)), hint: "only http(s) URLs are honored — falling back to the
|
|
207
|
+
logger?.warn("sema_registry_model_base_url_rejected", { model: m.name, baseUrl: buildDisplaySafeUrl(String(rawBaseUrl)), hint: "only http(s) URLs are honored — falling back to the deployment gateway (MODEL_GATEWAY_BASEURL); with none declared the whole candidate is refused (model-route-endpoint)" });
|
|
207
208
|
safeUrl = undefined;
|
|
208
209
|
}
|
|
209
210
|
}
|
|
@@ -264,6 +265,11 @@ function stageEffective(config, eff, logger, opts) {
|
|
|
264
265
|
const activeTiers = resolveActiveTiers(eff.models) ?? {};
|
|
265
266
|
const picked = resolveDefaultModelName(eff, (n) => models[n] !== undefined, enabled[0].name, (source, name) => logger?.warn("sema_registry_default_dangling", { source, name, hint: "explicit default names a model that is not in the enabled catalog — falling to the next source" }));
|
|
266
267
|
models.default = models[picked.name];
|
|
268
|
+
const endpointRefusal = crossInvariant("model-route-endpoint").check({ live: config, next: { ...config, models } });
|
|
269
|
+
if (endpointRefusal !== undefined) {
|
|
270
|
+
throw new Error(`candidate model plane refused whole (cross-invariant model-route-endpoint): ${endpointRefusal} ` +
|
|
271
|
+
"The current generation keeps serving; the candidate is re-judged on the next pull.");
|
|
272
|
+
}
|
|
267
273
|
const atModelAllowlist = stageAtModelAllowlist(eff.models?.atModelAllowlist, models, logger);
|
|
268
274
|
modelPlane = {
|
|
269
275
|
models,
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type CenterCredentialSource } from "../center-credential.js";
|
|
2
|
+
export declare const SCHEMA_MAJOR_HEADER = "x-sema-schema-major";
|
|
3
|
+
/** 已装 settings-schema 的 major(机器派生)。版本号不成形 ⇒ 模块装载即抛(装坏了的包不该带着一个猜出来的
|
|
4
|
+
* major 去跟中心协商 wire 形)。 */
|
|
5
|
+
export declare const SETTINGS_SCHEMA_MAJOR: string;
|
|
6
|
+
/** 一个中心端点 = 基址 + 凭证来源(`ServiceConfig.configCenter` 结构上满足)。 */
|
|
7
|
+
export interface CenterEndpoint {
|
|
8
|
+
readonly baseUrl: string;
|
|
9
|
+
readonly credential: CenterCredentialSource;
|
|
10
|
+
}
|
|
11
|
+
/** 中心路由闭集。 */
|
|
12
|
+
export type CenterRoute = {
|
|
13
|
+
readonly kind: "effective";
|
|
14
|
+
readonly worker?: string | undefined;
|
|
15
|
+
readonly principal?: string | undefined;
|
|
16
|
+
} | {
|
|
17
|
+
readonly kind: "skill-content";
|
|
18
|
+
readonly contentHash: string;
|
|
19
|
+
} | {
|
|
20
|
+
readonly kind: "prompt-artifact";
|
|
21
|
+
readonly digest: string;
|
|
22
|
+
} | {
|
|
23
|
+
readonly kind: "prompt-blob";
|
|
24
|
+
readonly digest: string;
|
|
25
|
+
} | {
|
|
26
|
+
readonly kind: "fleet-leases";
|
|
27
|
+
} | {
|
|
28
|
+
readonly kind: "fleet-instances";
|
|
29
|
+
} | {
|
|
30
|
+
readonly kind: "fleet-usage";
|
|
31
|
+
} | {
|
|
32
|
+
readonly kind: "execution-keys";
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* 拉取视图(`/api/config/effective`)查询串的**唯一拼装点**:`?principal=`(per-principal 视图)、`?worker=`
|
|
36
|
+
* (worker 作用域视图;对 `wpt_<self>` 是授权必需,对全量令牌无害)。
|
|
37
|
+
* [ref] E-15:执行体绑 scope 的参数将来加在这里(契约字段 settings-schema 6.0.0;本批不铸)。
|
|
38
|
+
*/
|
|
39
|
+
export declare function pullViewQuery(view: {
|
|
40
|
+
readonly worker?: string | undefined;
|
|
41
|
+
readonly principal?: string | undefined;
|
|
42
|
+
}): string;
|
|
43
|
+
export interface CenterRequestInit {
|
|
44
|
+
readonly method?: "GET" | "POST" | "DELETE";
|
|
45
|
+
/** JSON 请求体(在场 ⇒ `content-type: application/json`)。 */
|
|
46
|
+
readonly json?: unknown;
|
|
47
|
+
/** 条件请求(`if-none-match`)。 */
|
|
48
|
+
readonly etag?: string | undefined;
|
|
49
|
+
readonly timeoutMs: number;
|
|
50
|
+
readonly fetchImpl?: typeof fetch;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 对中心发一条请求。返回响应与**这次请求所用凭证的身份**(调用方据此给拿回来的候选打分箱标记,
|
|
54
|
+
* 见 `boot/config-center.ts` 的 LKG / prompt 状态分箱)。
|
|
55
|
+
*/
|
|
56
|
+
export declare function centerFetch(center: CenterEndpoint, route: CenterRoute, init: CenterRequestInit): Promise<{
|
|
57
|
+
res: Response;
|
|
58
|
+
identity: string;
|
|
59
|
+
}>;
|
|
60
|
+
//# sourceMappingURL=center-request.d.ts.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import settingsSchemaPackage from "@sema-agent/settings-schema/package.json" with { type: "json" };
|
|
2
|
+
import { CenterCredentialUnavailableError } from "../center-credential.js";
|
|
3
|
+
export const SCHEMA_MAJOR_HEADER = "x-sema-schema-major";
|
|
4
|
+
export const SETTINGS_SCHEMA_MAJOR = (() => {
|
|
5
|
+
const version = settingsSchemaPackage.version;
|
|
6
|
+
const m = /^(\d+)\.\d+\.\d+/.exec(version);
|
|
7
|
+
if (m === null)
|
|
8
|
+
throw new Error(`cannot derive ${SCHEMA_MAJOR_HEADER}: @sema-agent/settings-schema package.json has version ${JSON.stringify(version)}`);
|
|
9
|
+
return m[1];
|
|
10
|
+
})();
|
|
11
|
+
export function pullViewQuery(view) {
|
|
12
|
+
const parts = [];
|
|
13
|
+
if (view.principal !== undefined)
|
|
14
|
+
parts.push(`principal=${encodeURIComponent(view.principal)}`);
|
|
15
|
+
if (view.worker)
|
|
16
|
+
parts.push(`worker=${encodeURIComponent(view.worker)}`);
|
|
17
|
+
return parts.length === 0 ? "" : `?${parts.join("&")}`;
|
|
18
|
+
}
|
|
19
|
+
function contentRoot(kind) {
|
|
20
|
+
switch (kind) {
|
|
21
|
+
case "user-jwt":
|
|
22
|
+
return "/api/v1/me";
|
|
23
|
+
case "pull-token":
|
|
24
|
+
case "none":
|
|
25
|
+
return "/api/config";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function routePath(route, kind) {
|
|
29
|
+
switch (route.kind) {
|
|
30
|
+
case "effective":
|
|
31
|
+
return `/api/config/effective${pullViewQuery(route)}`;
|
|
32
|
+
case "skill-content":
|
|
33
|
+
return `${contentRoot(kind)}/skills/content/${encodeURIComponent(route.contentHash)}`;
|
|
34
|
+
case "prompt-artifact":
|
|
35
|
+
return `${contentRoot(kind)}/prompts/epoch-artifacts/${encodeURIComponent(route.digest)}`;
|
|
36
|
+
case "prompt-blob":
|
|
37
|
+
return `${contentRoot(kind)}/prompts/blobs/${encodeURIComponent(route.digest)}`;
|
|
38
|
+
case "fleet-leases":
|
|
39
|
+
return "/api/fleet/leases";
|
|
40
|
+
case "fleet-instances":
|
|
41
|
+
return "/api/fleet/instances";
|
|
42
|
+
case "fleet-usage":
|
|
43
|
+
return "/api/fleet/usage";
|
|
44
|
+
case "execution-keys":
|
|
45
|
+
return "/api/config/execution-keys";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export async function centerFetch(center, route, init) {
|
|
49
|
+
const scheme = new URL(center.baseUrl).protocol;
|
|
50
|
+
if (scheme !== "http:" && scheme !== "https:")
|
|
51
|
+
throw new Error(`SEMA_REGISTRY_URL must be http(s), got "${scheme}"`);
|
|
52
|
+
const cred = center.credential.read();
|
|
53
|
+
if (!cred.ok)
|
|
54
|
+
throw new CenterCredentialUnavailableError(cred.reason);
|
|
55
|
+
const headers = { [SCHEMA_MAJOR_HEADER]: SETTINGS_SCHEMA_MAJOR };
|
|
56
|
+
if (cred.kind !== "none")
|
|
57
|
+
headers.authorization = `Bearer ${cred.bearer}`;
|
|
58
|
+
if (init.json !== undefined)
|
|
59
|
+
headers["content-type"] = "application/json";
|
|
60
|
+
if (init.etag)
|
|
61
|
+
headers["if-none-match"] = init.etag;
|
|
62
|
+
const url = `${center.baseUrl.replace(/\/+$/, "")}${routePath(route, cred.kind)}`;
|
|
63
|
+
const res = await (init.fetchImpl ?? fetch)(url, {
|
|
64
|
+
method: init.method ?? "GET",
|
|
65
|
+
headers,
|
|
66
|
+
...(init.json !== undefined ? { body: JSON.stringify(init.json) } : {}),
|
|
67
|
+
signal: AbortSignal.timeout(init.timeoutMs),
|
|
68
|
+
});
|
|
69
|
+
return { res, identity: cred.identity };
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=center-request.js.map
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
* (core 1.45), so each model/cascade-rung authenticates with its own upstream key.
|
|
16
16
|
*
|
|
17
17
|
* Per-model `baseUrl` IS transported (a catalog model may live on a different gateway;
|
|
18
|
-
* core brain honors model.baseUrl, absent = "" =
|
|
18
|
+
* core brain honors model.baseUrl, absent = "" = the deployment gateway `MODEL_GATEWAY_BASEURL`; S-623: with no gateway
|
|
19
|
+
* declared, a candidate carrying such an openai-lane entry is refused whole — cross-invariant `model-route-endpoint`).
|
|
19
20
|
*
|
|
20
21
|
* Hot-reload status (复审 2026-07-29 P1-11 — 亲读判定,取代此处旧的 "models/roles are restart-to-apply"
|
|
21
22
|
* TODO, which went stale when `mutateInPlace` landed):
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type EffectiveReadWarning, type EntitlementRuntimeCaps } from "@sema-agent/settings-schema";
|
|
2
2
|
import type { ScenarioRuling } from "../capabilities/scenarios.js";
|
|
3
3
|
import type { EffectiveConfig, ExecutionRuling } from "./types.js";
|
|
4
|
+
import { type CenterEndpoint } from "./center-request.js";
|
|
4
5
|
/** GET the effective config (Bearer + ETag). null = 304 (unchanged). Throws on transport/HTTP error, and on a
|
|
5
6
|
* payload that is not an effective config at all(非对象 / version 非有限数 / gate 域坏形——[ref] 裁B)。
|
|
6
7
|
* `domainErrors`:坏 catalog 域(schema default 已落)逐域单列——与本地腿 `FetchEffectiveResult` 同键同义,
|
|
@@ -12,8 +13,11 @@ import type { EffectiveConfig, ExecutionRuling } from "./types.js";
|
|
|
12
13
|
* `legacy-governance-lifted` 铸出来,理由逐字写在 `legacyGovernanceFromRuntime` 的 SUNSET 注里:
|
|
13
14
|
* 「关窗的前提是有人知道它还开着」。两条读腿各返一份原流,那句话在本仓才成立。
|
|
14
15
|
* 🔐 原流的 `error` 字段**未经** `redactConfigError`(那道脱敏挂在真正进日志流的 `domainErrors` 产出边界上)
|
|
15
|
-
* —— 任何把本键路由进结构化日志/审计流的消费者必须自己先脱敏。
|
|
16
|
-
|
|
16
|
+
* —— 任何把本键路由进结构化日志/审计流的消费者必须自己先脱敏。
|
|
17
|
+
*
|
|
18
|
+
* `credentialIdentity`(S-651 / [ref] E-15):这次拉取所用凭证的身份(`center-credential.ts`)。调用方拿它给
|
|
19
|
+
* 候选打分箱标记 —— LKG / prompt 活动状态只许落进**拉到它的那个身份**的箱。 */
|
|
20
|
+
export declare function fetchEffective(center: CenterEndpoint, etag: string | undefined, fetchImpl?: typeof fetch, worker?: string): Promise<{
|
|
17
21
|
effective: EffectiveConfig;
|
|
18
22
|
etag?: string;
|
|
19
23
|
domainErrors?: Array<{
|
|
@@ -21,6 +25,7 @@ export declare function fetchEffective(baseUrl: string, token: string, etag: str
|
|
|
21
25
|
error: string;
|
|
22
26
|
}>;
|
|
23
27
|
readWarnings: readonly EffectiveReadWarning[];
|
|
28
|
+
credentialIdentity: string;
|
|
24
29
|
} | null>;
|
|
25
30
|
/**
|
|
26
31
|
* [ref] §K (core 1.157 `RunnerDeps.runtimeCapsResolver`) — GET the PER-PRINCIPAL runtime
|
|
@@ -41,7 +46,7 @@ export declare function fetchEffective(baseUrl: string, token: string, etag: str
|
|
|
41
46
|
* fail-closes (core then DENIES the optional `allowWorkflows` cap — an outage must never silently GRANT). ETag is
|
|
42
47
|
* content-hashed (`p-<hash>`); an `if-none-match` poll is cheap. Mirrors `fetchEffective` (Bearer + 8s + scheme guard).
|
|
43
48
|
*/
|
|
44
|
-
export declare function fetchPrincipalCaps(
|
|
49
|
+
export declare function fetchPrincipalCaps(center: CenterEndpoint, principal: string, etag: string | undefined, fetchImpl?: typeof fetch, worker?: string): Promise<{
|
|
45
50
|
runtimeCaps: EntitlementRuntimeCaps | null;
|
|
46
51
|
configured: boolean;
|
|
47
52
|
scenario?: ScenarioRuling;
|
|
@@ -68,7 +73,7 @@ export declare function fetchPrincipalCaps(baseUrl: string, token: string, princ
|
|
|
68
73
|
* 段在场 ⇒ 原值(空表 = 取数成功的负结果,与缺席不同格)。条件请求不适用(本腿恒不带 etag),
|
|
69
74
|
* 因此 304 在传输层就是 fail-loud。
|
|
70
75
|
*/
|
|
71
|
-
export declare function fetchPrincipalOrgMemory(
|
|
76
|
+
export declare function fetchPrincipalOrgMemory(center: CenterEndpoint, principal: string, fetchImpl?: typeof fetch, worker?: string): Promise<unknown>;
|
|
72
77
|
/** An HTTP error from a config-center fetch that carries the response status so callers can branch on it
|
|
73
78
|
* (e.g. translate a 404 to "unknown hash → undefined") WITHOUT parsing the message string. */
|
|
74
79
|
export declare class ConfigCenterHttpError extends Error {
|
|
@@ -76,18 +81,21 @@ export declare class ConfigCenterHttpError extends Error {
|
|
|
76
81
|
constructor(message: string, status: number);
|
|
77
82
|
}
|
|
78
83
|
/**
|
|
79
|
-
* Fetch one skill's body by content hash (`GET
|
|
84
|
+
* Fetch one skill's body by content hash (`GET <content root>/skills/content/<hash>`, Bearer; the root is
|
|
85
|
+
* `/api/v1/me` for a user-JWT credential and `/api/config` otherwise — center-request.ts) and VERIFY its
|
|
80
86
|
* sha256 matches the manifest hash (integrity + rewrite check). The content is immutable per hash, so the
|
|
81
87
|
* caller caches by hash and never re-fetches an unchanged skill. Throws a {@link ConfigCenterHttpError}
|
|
82
88
|
* (carrying `.status`) on an HTTP error — INCLUDING 404 — or a plain Error on hash mismatch. Callers that
|
|
83
89
|
* want "unknown hash → undefined" parity with the local backend inspect `.status === 404` and swallow it;
|
|
84
90
|
* the legacy `applyCenterSkills` path keeps treating every throw as "skip this skill, keep baseline".
|
|
85
91
|
*/
|
|
86
|
-
export declare function fetchSkillContent(
|
|
92
|
+
export declare function fetchSkillContent(center: CenterEndpoint, contentHash: string, fetchImpl?: typeof fetch): Promise<string>;
|
|
87
93
|
/** [ref]b / [ref]① — §9.4 by-digest 补拉二函(fetchSkillContent 同姿势:HTTP+Bearer+8s 超时+
|
|
88
94
|
* 消费侧自证)。artifact=envelope JSON(验真=调用方过 core verifyPromptArtifact,canonical 重算——
|
|
89
95
|
* 本函数只做传输与形读,不超范围解释);blob=段文本 raw-byte(sha256 逐位自证,fetchSkillContent 同款)。
|
|
90
|
-
* digest 参数收带前缀/裸 hex 两形([ref] 参数口径)。
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
* digest 参数收带前缀/裸 hex 两形([ref] 参数口径)。路径根按凭证种类(B-5,center-request.ts)。
|
|
97
|
+
* ⚠️ 现状(S-651 取证车MS 总表第 8 条):`fetchPromptBlob` 在 `src/` 里**没有生产调用点**,真走得到的内容
|
|
98
|
+
* 拉取只有 skill 正文与 prompt 产物两条;它与另两条共用同一张路由表,本批不为它新接线。 */
|
|
99
|
+
export declare function fetchPromptArtifact(center: CenterEndpoint, artifactDigest: string, fetchImpl?: typeof fetch): Promise<unknown>;
|
|
100
|
+
export declare function fetchPromptBlob(center: CenterEndpoint, contentDigest: string, fetchImpl?: typeof fetch): Promise<string>;
|
|
93
101
|
//# sourceMappingURL=http-client.d.ts.map
|