@sema-agent/server 7.129.1 → 7.131.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 +102 -0
- package/README.md +8 -5
- package/README.zh-CN.md +6 -4
- package/USAGE.md +48 -9
- package/dist/audit.js +4 -1
- package/dist/bench/s1/live-deps.js +2 -0
- package/dist/boot/resolve-spec.js +43 -16
- package/dist/boot/stage-02-web-admin-pull.js +9 -1
- package/dist/boot/stage-10-http-server.d.ts +1 -1
- package/dist/boot/stage-10-http-server.js +7 -2
- package/dist/capabilities/builtin-tools.js +2 -1
- package/dist/capabilities/composite-members.d.ts +1 -0
- package/dist/capabilities/composite-members.js +1 -0
- package/dist/capabilities/hands-lane.js +2 -2
- package/dist/capabilities/host-tool-names.d.ts +17 -0
- package/dist/capabilities/host-tool-names.js +15 -0
- package/dist/capabilities/mcp-probe.js +19 -6
- package/dist/capabilities/repo-tools.js +4 -3
- package/dist/capabilities/scenarios.d.ts +34 -0
- package/dist/capabilities/scenarios.js +20 -4
- package/dist/capabilities/select-environment-tool.js +2 -1
- package/dist/capabilities/send-user-file-tool.d.ts +0 -1
- package/dist/capabilities/send-user-file-tool.js +2 -2
- package/dist/capabilities/skills.js +1 -1
- package/dist/capabilities/tool-defer.js +2 -1
- package/dist/config-catalog.js +16 -5
- package/dist/config-invariants.js +2 -2
- package/dist/config-provider.js +2 -2
- package/dist/config-types.d.ts +6 -0
- package/dist/config.d.ts +2 -2
- package/dist/config.js +18 -17
- package/dist/elicitation.d.ts +7 -0
- package/dist/elicitation.js +20 -5
- package/dist/exec-bounds.d.ts +6 -6
- package/dist/hooks/hook-llm.d.ts +11 -1
- package/dist/hooks/hook-llm.js +11 -14
- package/dist/hooks/hook-runner.d.ts +55 -9
- package/dist/hooks/hook-runner.js +86 -16
- package/dist/http/admission.js +2 -2
- package/dist/http/approval-sweeps.js +2 -2
- package/dist/http/checkpoint-ctx.d.ts +12 -11
- package/dist/http/checkpoint-ctx.js +4 -4
- package/dist/http/dispatch.js +1 -1
- package/dist/http/resume-legs.js +6 -5
- package/dist/http/routes/approvals-assistant.js +1 -1
- package/dist/http/routes/capabilities.js +13 -5
- package/dist/http/routes/runs.js +18 -14
- package/dist/http/routes/sessions.js +22 -10
- package/dist/http/routes/tasks.js +16 -11
- package/dist/http/scenario-read-face.d.ts +14 -0
- package/dist/http/scenario-read-face.js +5 -0
- package/dist/http/send.d.ts +10 -0
- package/dist/http/send.js +6 -0
- package/dist/http/server.d.ts +16 -4
- package/dist/http/server.js +2 -2
- package/dist/http/stored-body-replay.d.ts +10 -3
- package/dist/http/stored-body-replay.js +27 -6
- package/dist/http/wire-types.d.ts +18 -5
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/mcp-assertion-header.d.ts +2 -2
- package/dist/mcp-assertion-header.js +3 -3
- package/dist/model-entry-refusal.d.ts +1 -7
- package/dist/model-route-endpoint.d.ts +51 -11
- package/dist/model-route-endpoint.js +45 -23
- package/dist/model-route-gap.d.ts +5 -4
- package/dist/model-route-gap.js +1 -1
- package/dist/model-select.d.ts +58 -0
- package/dist/model-select.js +31 -0
- package/dist/namespaced-rule-name.d.ts +7 -4
- package/dist/namespaced-rule-name.js +4 -3
- package/dist/observability/fail-open.d.ts +16 -7
- package/dist/observability/fail-open.js +10 -2
- package/dist/plugins/local-session-store.js +2 -2
- package/dist/plugins/one-time-migrations.d.ts +21 -1
- package/dist/plugins/one-time-migrations.js +33 -1
- package/dist/plugins/remote-env-adb.js +5 -4
- package/dist/plugins/remote-env-local-docker.js +5 -4
- package/dist/plugins/remote-env-ssh.js +5 -4
- package/dist/question.d.ts +18 -9
- package/dist/question.js +21 -13
- package/dist/request-key-closure.d.ts +2 -2
- package/dist/request-key-closure.js +16 -9
- package/dist/rule-entry-list.d.ts +124 -17
- package/dist/rule-entry-list.js +140 -24
- package/dist/run-local.d.ts +3 -3
- package/dist/run-local.js +8 -7
- package/dist/runs.d.ts +42 -2
- package/dist/runs.js +45 -2
- package/dist/runtime-caps-resolver.d.ts +7 -0
- package/dist/runtime-caps-resolver.js +1 -0
- package/dist/session-titler.d.ts +4 -1
- package/dist/session-titler.js +13 -3
- package/dist/spec-fields.d.ts +8 -4
- package/dist/stored-request-hooks-m20.d.ts +23 -0
- package/dist/stored-request-hooks-m20.js +124 -0
- package/dist/stored-request-tool-axes-m19.d.ts +29 -0
- package/dist/stored-request-tool-axes-m19.js +27 -0
- package/dist/task-mcp.d.ts +55 -60
- package/dist/task-mcp.js +197 -96
- package/dist/task-settings.d.ts +41 -5
- package/dist/task-settings.js +39 -25
- package/dist/tool-axis-words.d.ts +14 -17
- package/dist/trace/core-keyset-guard.d.ts +2 -2
- package/dist/trace/ledger-sink.d.ts +29 -0
- package/dist/trace/ledger-sink.js +11 -0
- package/dist/trace/project.d.ts +6 -0
- package/dist/trace/project.js +11 -0
- package/dist/trace/wire-projection-faces.d.ts +1 -1
- package/dist/trace/wire-projection-faces.js +1 -1
- package/dist/user-authored-text.d.ts +7 -0
- package/dist/user-authored-text.js +3 -0
- package/dist/web-admin-client/apply-effective.js +10 -5
- package/dist/web-admin-client/skills-mcp.d.ts +1 -1
- package/dist/web-admin-client/types.d.ts +1 -1
- package/package.json +2 -2
- package/scripts/drain-check-local.mjs +3 -2
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { redactErrorMessage, redactHead, redactTerminalResult } from "../../observability/run-terminal.js";
|
|
2
2
|
import { runWithVerification, runCascade, taskNotificationDedupKey, uuidv7 } from "@sema-agent/core";
|
|
3
|
-
import { graftParkToolCallId, markChildrenStoppedByUserOnAbort, syncRefusalHttpStatus, releaseKeyIfZeroWorkRefusal, HEARTBEAT_MS } from "../../runs.js";
|
|
3
|
+
import { graftParkToolCallId, markChildrenStoppedByUserOnAbort, syncRefusalHttpStatus, releaseKeyIfZeroWorkRefusal, createPromptCommitReader, HEARTBEAT_MS } from "../../runs.js";
|
|
4
4
|
import { sealResultForExit, outwardPlane } from "../../terminal.js";
|
|
5
5
|
import { withPrincipal } from "../../observability/principal-context.js";
|
|
6
6
|
import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals, isFleetAgentTerminalNotification } from "../../fleet/fleet-bus.js";
|
|
7
7
|
import { defaultSubagentTailBus } from "../../fleet/subagent-tail-bus.js";
|
|
8
8
|
import { createReasoningSegments, reasoningStreamOfTask } from "../../trace/model-segment.js";
|
|
9
9
|
import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKeyOf, NotifiedKeys } from "../../orchestration/workflow-completion-inbox.js";
|
|
10
|
-
import { createLedgerSink } from "../../trace/ledger-sink.js";
|
|
10
|
+
import { createLedgerSink, createLedgerTwinEmit } from "../../trace/ledger-sink.js";
|
|
11
11
|
import { registerEngineNoticeLeg } from "../../trace/engine-notice-wire.js";
|
|
12
12
|
import { clearTurnActivity, readTurnActivityMs, recordTurnActivity } from "../../turn-activity.js";
|
|
13
13
|
import { buildCancelledLedgerRow, createRunAbort, mintCancelledResult } from "../../run-cancel-context.js";
|
|
@@ -249,6 +249,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
249
249
|
let durableHeartbeat;
|
|
250
250
|
const cancelledResult = (errorMessage, cause) => mintCancelledResult({ taskId: durableTaskId ?? "", sessionId: prepared.spec.sessionId ?? "", startedAtMs: streamLegStartedAt, message: errorMessage, cause });
|
|
251
251
|
let userMsgEntryId;
|
|
252
|
+
const promptCommit = createPromptCommitReader();
|
|
252
253
|
let anchorPut = false;
|
|
253
254
|
const putRewindAnchor = () => {
|
|
254
255
|
if (anchorPut || !userMsgEntryId || !durableTaskId || !prepared.spec.sessionId || !deps.resumeAnchorStore)
|
|
@@ -457,8 +458,9 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
457
458
|
break;
|
|
458
459
|
for (const end of ev.type === "done" ? [...childReasoning.cut(), ...topReasoning.cut()] : topReasoning.onEvent(ev))
|
|
459
460
|
sseData(res, end);
|
|
460
|
-
|
|
461
|
-
|
|
461
|
+
const promptEntryId = promptCommit(ev);
|
|
462
|
+
if (promptEntryId !== undefined)
|
|
463
|
+
userMsgEntryId = promptEntryId;
|
|
462
464
|
const causeAtDone = ev.type === "done" ? ac.cause() : undefined;
|
|
463
465
|
await deps.userInputLedger?.observeLegEvent(liveStream, ev);
|
|
464
466
|
prepared.acceptance.observe(ev);
|
|
@@ -594,6 +596,14 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
594
596
|
}
|
|
595
597
|
res.write(`event: ${frame.type}\ndata: ${JSON.stringify(frame)}\n\n`);
|
|
596
598
|
};
|
|
599
|
+
const faceAlive = () => !res.writableEnded && !res.destroyed;
|
|
600
|
+
const emitElicitation = createLedgerTwinEmit({ sink: ledgerSink, writeLive: emitAsk, liveReachable: faceAlive });
|
|
601
|
+
const writeQuestionLive = (frame) => {
|
|
602
|
+
if (!faceAlive())
|
|
603
|
+
throw new Error("live stream ended — question frame undeliverable");
|
|
604
|
+
emitAsk(frame);
|
|
605
|
+
};
|
|
606
|
+
const emitQuestion = createLedgerTwinEmit({ sink: ledgerSink, writeLive: writeQuestionLive, liveReachable: faceAlive });
|
|
597
607
|
await emitPendingWorkflowCompletions(deps.workflowCompletionInbox, prepared.spec.sessionId, askOwner, (frame) => {
|
|
598
608
|
if (closed || res.writableEnded || res.destroyed)
|
|
599
609
|
throw new Error("stream ended before the completion frame was written");
|
|
@@ -604,15 +614,10 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
604
614
|
sseData(res, frame);
|
|
605
615
|
}, { route: "sync-stream-open", connection: "live-sse", log: (m, x) => deps.logger?.info?.(m, x) });
|
|
606
616
|
const withElicit = deps.elicitation
|
|
607
|
-
? () => deps.elicitation.runWithContext({ taskId: askTaskId, owner: askOwner, emit:
|
|
617
|
+
? () => deps.elicitation.runWithContext({ taskId: askTaskId, owner: askOwner, emit: emitElicitation, abortSignal: ac.signal }, streamBody)
|
|
608
618
|
: streamBody;
|
|
609
|
-
const emitQuestion = (frame) => {
|
|
610
|
-
if (res.writableEnded || res.destroyed)
|
|
611
|
-
throw new Error("live stream ended — question frame undeliverable");
|
|
612
|
-
emitAsk(frame);
|
|
613
|
-
};
|
|
614
619
|
const withQuestion = () => deps.question
|
|
615
|
-
? deps.question.runWithContext({ taskId: askTaskId, owner: askOwner, emit: emitQuestion, abortSignal: ac.signal, deliverable:
|
|
620
|
+
? deps.question.runWithContext({ taskId: askTaskId, owner: askOwner, emit: emitQuestion, abortSignal: ac.signal, deliverable: faceAlive }, withElicit)
|
|
616
621
|
: withElicit();
|
|
617
622
|
const emitApproval = (frame) => {
|
|
618
623
|
if (res.writableEnded || res.destroyed)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 「本部署没有这个场景」的唯一答复(A9 叶:只依赖 `send.ts`)—— 场景详情读面(`GET /v1/capabilities/scenarios/:name`,名字不在 `scenarioDetails` 里)答它。
|
|
3
|
+
*
|
|
4
|
+
* 回答「此主体在此场景能看见哪些 MCP 服务器 / A2A 对端」的读口不经过这里:它们的场景名只由 `deps.scenarioGate` 给
|
|
5
|
+
* (`ServiceCoreDeps` 的必填件,`capabilities/scenarios.ts` `createScenarioGate` 铸的那一只),点名与缺席都问,拒就是门的 typed 错 —— 读口没有第二个场景名来源。
|
|
6
|
+
*/
|
|
7
|
+
import type { ServerResponse } from "node:http";
|
|
8
|
+
/**
|
|
9
|
+
* 「本部署没有这个场景」的唯一答复。一句话、一个码,不各写一份。
|
|
10
|
+
* [ref] 信封统一:本答复曾是全站唯一对象形 error(`{error:{code}}`)——SDK toApiError 对它特判过一轮 TypeError 加固;统一为「字符串 error + 顶层 errorCode」邻位形
|
|
11
|
+
* (cancel/decide 同款),机器判据保留。
|
|
12
|
+
*/
|
|
13
|
+
export declare function sendScenarioNotFound(res: ServerResponse): void;
|
|
14
|
+
//# sourceMappingURL=scenario-read-face.d.ts.map
|
package/dist/http/send.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { ServerResponse } from "node:http";
|
|
11
11
|
import { type SessionErrorFace } from "../session-error-wire.js";
|
|
12
|
+
import type { HttpError } from "../security.js";
|
|
12
13
|
/**
|
|
13
14
|
* [ref](黑板 [ref]②)—— 一条响应「是 SSE 流」的**在场标记**。
|
|
14
15
|
*
|
|
@@ -74,6 +75,15 @@ export declare function sendError(res: ServerResponse, status: number, errorCode
|
|
|
74
75
|
* throw 点里只有 3 个带。为了让「errorCode 恒在」在这些站点也成立(而不是靠豁免表放行),按**状态码**
|
|
75
76
|
* 给一个诚实的粗码:它只声明「这类拒绝」,不冒充比 throw 点更细的语义。带 `code` 的 throw 点原样优先。 */
|
|
76
77
|
export declare function httpErrorCode(status: number, code?: string): string;
|
|
78
|
+
/**
|
|
79
|
+
* 一只 `HttpError` 的**拒体成员**:`{ error, errorCode, ...extra }` —— 码经 {@link httpErrorCode}(带码的 throw 原样优先,否则按状态给粗码),
|
|
80
|
+
* 句 = `message`,`extra`(拒的机读事实,如场景门的 `detail: { scenario, source }`)原样随体、排在最后。**全仓唯一组句器**:整面拒({@link sendHttpError}:
|
|
81
|
+
* 全局兜底 catch 与准入段的 typed 拒)与把同一只拒当**数据**带进 200 体的面板(`GET /v1/sessions/:id/mcp` 的 `scenarioRefusal`)读的是它 ——
|
|
82
|
+
* 同一只错在两处渲出的成员逐字同,面板不自己拼 `{ errorCode, error }`(拼了就丢 `extra`:壳分不出这次门拒是中心指派漂了还是部署缺省悬空)。
|
|
83
|
+
*/
|
|
84
|
+
export declare function httpErrorBody(e: HttpError): Record<string, unknown>;
|
|
85
|
+
/** 整面拒:状态 = `e.status`,体 = {@link httpErrorBody}。 */
|
|
86
|
+
export declare function sendHttpError(res: ServerResponse, e: HttpError): void;
|
|
77
87
|
/**
|
|
78
88
|
* 🔴 B1 / S-535 —— `SessionPolicyError` → 应答,经 {@link sessionPolicyErrorHttp} 那**一只**映射
|
|
79
89
|
* (`src/session-policy-wire.ts`)。回 `true` = 已应答,调用点 `return`。
|
package/dist/http/send.js
CHANGED
|
@@ -48,6 +48,12 @@ export function httpErrorCode(status, code) {
|
|
|
48
48
|
default: return status >= 500 ? "internal.error" : "request.rejected";
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
+
export function httpErrorBody(e) {
|
|
52
|
+
return { error: e.message, errorCode: httpErrorCode(e.status, e.code), ...e.extra };
|
|
53
|
+
}
|
|
54
|
+
export function sendHttpError(res, e) {
|
|
55
|
+
sendJson(res, e.status, httpErrorBody(e));
|
|
56
|
+
}
|
|
51
57
|
export function sendSessionPolicyError(res, e, sentencePrefix = "") {
|
|
52
58
|
if (!(e instanceof SessionPolicyError))
|
|
53
59
|
return false;
|
package/dist/http/server.d.ts
CHANGED
|
@@ -58,7 +58,7 @@ export interface RequestAuth {
|
|
|
58
58
|
* 平铺形的直接后果 = main.ts 一个上千行的装配字面量,键与键之间没有任何「属于同一件事」的编译期表达,
|
|
59
59
|
* 键落错家/从未接线只能靠源码钉逐个盯(1.300/1.301/1.304/1.307/1.310 五起真缺陷都是这个病族)。
|
|
60
60
|
*
|
|
61
|
-
* 药:七个**窄**接口 +
|
|
61
|
+
* 药:七个**窄**接口 + 顶层必填五件。装配点按组构造(每组一个带类型标注的中间 const ⇒ 受 TS 多余
|
|
62
62
|
* 属性检查保护),键放错组当场编译红。
|
|
63
63
|
*
|
|
64
64
|
* 兼容形(本批**不动消费点**):`ServiceDeps` = 平铺视图 ∩ 组槽。旧的平铺构造(96 个测试文件 +
|
|
@@ -66,8 +66,8 @@ export interface RequestAuth {
|
|
|
66
66
|
* 入口摊平回平铺视图,server.ts 内的 ~700 处 `deps.x` 消费点原样保留。消费点迁移是后批的事——
|
|
67
67
|
* 一步到位需要同批改 ~700 处 src + ~800 处 test 键位,与「先立形、后搬家」相比没有额外收益。
|
|
68
68
|
*/
|
|
69
|
-
/** createHttpServer
|
|
70
|
-
*
|
|
69
|
+
/** createHttpServer 的**必填五件**——引擎、部署配置、请求→TaskSpec 的映射、side-query 执行席、读口场景门。
|
|
70
|
+
* 五者恒在场,故留在顶层(不进任何可选组):没有它们 server 根本起不来,包一层可选组只会把编译期
|
|
71
71
|
* 错误推迟到运行期(本仓 E-HIGH-1 病族的成因就是「可选键从未接线、编译期无声」)。 */
|
|
72
72
|
export interface ServiceCoreDeps {
|
|
73
73
|
runner: Runner;
|
|
@@ -123,6 +123,18 @@ export interface ServiceCoreDeps {
|
|
|
123
123
|
* `runnerFor: () => runner` —— 那是一句表态,不是一个回落。
|
|
124
124
|
*/
|
|
125
125
|
runnerFor: (spec: TaskSpec) => Runner;
|
|
126
|
+
/**
|
|
127
|
+
* 读口的场景名过门 —— `capabilities/scenarios.ts` `createScenarioGate` 铸的**那一只**(与 `resolveSpec` 同一个工厂、同一张场景表、同一份
|
|
128
|
+
* `principalCaps`、同一份部署缺省):凡回答「此主体在此场景能看见哪些 MCP 服务器 / A2A 对端」的口 —— `GET /v1/capabilities/mcp` 的状态面、
|
|
129
|
+
* `GET /v1/sessions/:id/mcp` 与 `…/a2a` 两张会话面板 —— 的场景名都由它给,**点名与缺席都问**:点名 ⇒ 判在册 / allowlist,
|
|
130
|
+
* 拒体(400 `scenario_unknown` 附在册词表 / 400 `scenario_not_allowed` 附 allowlist)与任务提交腿逐字同;缺席 ⇒ 指派链(中心 ruling 胜者,
|
|
131
|
+
* 否则部署缺省),与这位主体 run 时同一读法(指派悬空 ⇒ 503 `state.scenario_assignment_unknown`,同提交腿)。
|
|
132
|
+
* 读面上这只门走**只读腿**(`createScenarioGate(…, "peek")`):问中心裁定时 center 抖动的那一拍,不替这个主体的真提交留下 fail-closed 降级项
|
|
133
|
+
* (提交腿自己是 `"enforce"`)。
|
|
134
|
+
* 🔴 **必填**:读口的场景名只有这一个来源,没有「缺接缝 ⇒ 读部署缺省 / 点名 404」这类第二种读法 —— 漏接是**编译错**(嵌入方自己装配时,工厂与它的型从包根导出:
|
|
135
|
+
* `createScenarioGate` / `ScenarioGate` / `ScenarioGateLeg` / `ScenarioGateSources`)。
|
|
136
|
+
*/
|
|
137
|
+
scenarioGate: import("../capabilities/scenarios.js").ScenarioGate;
|
|
126
138
|
}
|
|
127
139
|
/** **durable 持久面**:每个键 present ⇔ 对应路由/能力位在场,absent ⇒ 501(诚实缺席),
|
|
128
140
|
* 绝不静默降级。装配点 = main.ts 的 store backend 开箱段。 */
|
|
@@ -830,7 +842,7 @@ export interface ServiceDepGroups {
|
|
|
830
842
|
deployment?: ServiceDeploymentDeps;
|
|
831
843
|
knobs?: ServiceKnobDeps;
|
|
832
844
|
}
|
|
833
|
-
/** 平铺视图 =
|
|
845
|
+
/** 平铺视图 = 必填五件 + 其余七组的全可选并集。**派生**自组接口,故与分组形永不漂。 */
|
|
834
846
|
export type FlatServiceDeps = ServiceCoreDeps & Partial<ServiceStoreDeps & ServiceCoordinatorDeps & ServiceSeamDeps & ServiceObservabilityDeps & ServiceGovernanceDeps & ServiceDeploymentDeps & ServiceKnobDeps>;
|
|
835
847
|
/** createHttpServer 的入参:两形皆可(平铺=存量,分组=推荐)。同名键两形都给时,**顶层显式键赢**
|
|
836
848
|
* (含显式 `undefined`)——一处装配点只该用一形,混用时以肉眼最近的那个为准。 */
|
package/dist/http/server.js
CHANGED
|
@@ -17,7 +17,7 @@ import { cascadeConfig } from "./run-meta.js";
|
|
|
17
17
|
export { cascadeConfig };
|
|
18
18
|
import { coarseStatusForState, errorCodeForExit } from "./routes/images.js";
|
|
19
19
|
export { coarseStatusForState, errorCodeForExit };
|
|
20
|
-
import { sendError,
|
|
20
|
+
import { sendError, sendHttpError, sendSessionError, msg } from "./send.js";
|
|
21
21
|
import { createRequestGuards } from "./request-guards.js";
|
|
22
22
|
import { createAdmission, MAX_SKILL_CONTENT_CHARS, MAX_SYSTEM_PROMPT_CHARS, MAX_OUTPUT_SCHEMA_CHARS, validateUserSkills } from "./admission.js";
|
|
23
23
|
import { createResumeLegs } from "./resume-legs.js";
|
|
@@ -121,7 +121,7 @@ export function createHttpServer(rawDeps) {
|
|
|
121
121
|
void handle(req, res).catch((err) => {
|
|
122
122
|
if (err instanceof HttpError) {
|
|
123
123
|
if (!res.headersSent)
|
|
124
|
-
|
|
124
|
+
sendHttpError(res, err);
|
|
125
125
|
else
|
|
126
126
|
res.end();
|
|
127
127
|
return;
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { PermissionMode } from "@sema-agent/core";
|
|
11
11
|
import { type Autonomy } from "../runtime-governance.js";
|
|
12
|
+
import { type LaneHomeDeployment } from "../execution-lane-caps.js";
|
|
12
13
|
import { type StoredBodyReplayRefusal } from "./checkpoint-ctx.js";
|
|
13
14
|
import type { CheckpointCtx } from "./route-ctx.js";
|
|
14
15
|
/**
|
|
@@ -21,8 +22,14 @@ export declare function permissionModeReadingUnstated(ctx: CheckpointCtx, floor:
|
|
|
21
22
|
floor: PermissionMode;
|
|
22
23
|
} | undefined;
|
|
23
24
|
/**
|
|
24
|
-
*
|
|
25
|
-
* 任一问中都不重放,句子只需一句)。`autonomy` = 本部署**此刻**的 `config.autonomy`(活 config:热改即生效);地板由它的唯一属主算,问点不自己算。
|
|
25
|
+
* 预判读的部署配置:`autonomy`(第三问的模式词地板)与执行车道的 home 两位(`~/` 形规则那一问)。`ServiceConfig` 结构上满足,四个问点传 `deps.config` 整份。
|
|
26
26
|
*/
|
|
27
|
-
export
|
|
27
|
+
export type StoredReplayDeployment = LaneHomeDeployment & {
|
|
28
|
+
readonly autonomy?: Autonomy | undefined;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* 三问一只判据:不能重放 ⇒ 返回拒的那条规则;能 ⇒ `undefined`。顺序:allow 读法记号 → 请求设置里的条目表(**与拒点同序**:桶门〔形 / 空串 / 超帽〕→ 工具名清单读成空〔**只问限制方向**:放行方向的存量条目读成空,续跑照旧、只留一行运维 warn〕→ deny / ask 的名字层 + 规则文法)→ 模式词读法记号(几问同中时答先中的那一问 ——
|
|
32
|
+
* 任一问中都不重放,句子只需一句)。`deployment` = 本部署**此刻**的 `config`(活 config:热改即生效):`autonomy` 的地板由它的唯一属主算,`~/` 形规则的 home 由 `executionLaneHomeDir` 算,问点都不自己算。
|
|
33
|
+
*/
|
|
34
|
+
export declare function storedBodyReplayRefusal(ctx: CheckpointCtx, deployment: StoredReplayDeployment): StoredBodyReplayRefusal | undefined;
|
|
28
35
|
//# sourceMappingURL=stored-body-replay.d.ts.map
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { storedBodyPermissionMode } from "../task-settings.js";
|
|
1
|
+
import { REQUEST_SETTINGS_REFUSAL_PREFIX, rulePolicyBucketsRefusal, settingsPermissionBucketIssue, storedBodyPermissionMode } from "../task-settings.js";
|
|
2
2
|
import { autonomyModeFloor, entryPermissionMode } from "../runtime-governance.js";
|
|
3
|
-
import {
|
|
3
|
+
import { inertToolListEntryOf, replayInertEntry } from "../rule-entry-list.js";
|
|
4
|
+
import { executionLaneHomeDir } from "../execution-lane-caps.js";
|
|
5
|
+
import { httpErrorCode } from "./send.js";
|
|
4
6
|
import { allowListReadingEraOf, allowListReadingUnstated, permissionModeReadingStated } from "./checkpoint-ctx.js";
|
|
5
7
|
export function permissionModeReadingUnstated(ctx, floor) {
|
|
6
8
|
if (floor === undefined || permissionModeReadingStated(ctx))
|
|
@@ -8,14 +10,33 @@ export function permissionModeReadingUnstated(ctx, floor) {
|
|
|
8
10
|
const stored = storedBodyPermissionMode(ctx.body);
|
|
9
11
|
return entryPermissionMode(stored, floor).effective === stored ? undefined : { stored, floor };
|
|
10
12
|
}
|
|
11
|
-
|
|
13
|
+
const BUCKET_GATE_CODE = "request.field_invalid";
|
|
14
|
+
function storedPermissions(body) {
|
|
15
|
+
const permissions = body?.settings?.permissions;
|
|
16
|
+
return permissions !== null && typeof permissions === "object" && !Array.isArray(permissions) ? permissions : undefined;
|
|
17
|
+
}
|
|
18
|
+
function storedPermissionBucketRefusal(body) {
|
|
19
|
+
const issue = settingsPermissionBucketIssue(storedPermissions(body));
|
|
20
|
+
if (issue === null)
|
|
21
|
+
return undefined;
|
|
22
|
+
return replayInertEntry(issue.fields.field, issue.fields.field === "settings.permissions.allow" ? "allow" : "deny", BUCKET_GATE_CODE, issue.message, { ...issue.fields });
|
|
23
|
+
}
|
|
24
|
+
function storedRulePolicyRefusal(body, deployment) {
|
|
25
|
+
const permissions = storedPermissions(body);
|
|
26
|
+
if (permissions === undefined)
|
|
27
|
+
return undefined;
|
|
28
|
+
const home = executionLaneHomeDir(deployment);
|
|
29
|
+
const refused = rulePolicyBucketsRefusal({ ...(Array.isArray(permissions["ask"]) ? { ask: permissions["ask"] } : {}), ...(Array.isArray(permissions["deny"]) ? { deny: permissions["deny"] } : {}) }, home !== undefined ? { home } : {});
|
|
30
|
+
return refused === undefined ? undefined : replayInertEntry(`settings.permissions.${refused.bucket}`, "deny", httpErrorCode(422), `${REQUEST_SETTINGS_REFUSAL_PREFIX}${refused.message}`);
|
|
31
|
+
}
|
|
32
|
+
export function storedBodyReplayRefusal(ctx, deployment) {
|
|
12
33
|
const writer = ctx.writerVersion !== undefined ? { writerVersion: ctx.writerVersion } : {};
|
|
13
34
|
if (allowListReadingUnstated(ctx))
|
|
14
35
|
return { kind: "allow_list_reading", era: allowListReadingEraOf(ctx), ...writer };
|
|
15
|
-
const entry =
|
|
36
|
+
const entry = storedPermissionBucketRefusal(ctx.body) ?? inertToolListEntryOf(ctx.body, ["deny"]) ?? storedRulePolicyRefusal(ctx.body, deployment);
|
|
16
37
|
if (entry !== undefined)
|
|
17
|
-
return { kind: "
|
|
18
|
-
const mode = permissionModeReadingUnstated(ctx, autonomyModeFloor(autonomy));
|
|
38
|
+
return { kind: "inert_tool_list", entry };
|
|
39
|
+
const mode = permissionModeReadingUnstated(ctx, autonomyModeFloor(deployment.autonomy));
|
|
19
40
|
if (mode !== undefined)
|
|
20
41
|
return { kind: "permission_mode_reading", ...mode, ...writer };
|
|
21
42
|
return undefined;
|
|
@@ -33,6 +33,12 @@ export interface TaskRequestBody {
|
|
|
33
33
|
} | {
|
|
34
34
|
url: string;
|
|
35
35
|
}>;
|
|
36
|
+
/** 请求层用户附件(引擎 `TaskSpec.userAttachments`,至多 20 条):挂在**这次提交的目标**上,原样交给引擎 —— 每条以引擎铸的消息紧随目标送到模型,
|
|
37
|
+
* 交付结局在 `human_input{carrier:"objective"}.attachments` 报一次。条目形 = 引擎的 `UserAttachment`(`{ path, lines? }` 读本次 run 的工作区 /
|
|
38
|
+
* `{ name, text, startLine? }` / `{ name, image: { data, mimeType } }` 宿主持有的字节)。**形判属主 = 引擎**:本服务不另判,坏形 / 超 Read 上限的
|
|
39
|
+
* text / 过不了图片准入 / 无工作区的 path 由引擎在第一次模型调用之前拒 `config.user_attachment_invalid`(同步腿 400)。只随目标走:送话口不收
|
|
40
|
+
* (400 `request.body_shape`,键落 `unsupportedKeys`),续跑 / 投递腿的重建不带它(`http/resume-legs.ts` 的一次性键表)。能力位 `userAttachments`。 */
|
|
41
|
+
userAttachments?: TaskSpec["userAttachments"];
|
|
36
42
|
/** D-1 通用文件上传:预上传附件的句柄数组(POST /v1/attachments 的 `id`)。提交时绑定到会话并在
|
|
37
43
|
* 执行环境工作目录 `attachments/` 下物化;objective 尾部追加告知(名字/mime/尺寸,内容不进流)。
|
|
38
44
|
* 上限 16/task;未知或他人 id → 400(fresh 腿)。⚠️ 名为 `attachmentIds` 而非 `attachments`——
|
|
@@ -308,12 +314,15 @@ export interface TaskRequestBody {
|
|
|
308
314
|
* 坏形不挂键。不投递技能正文(正文照旧由 `Skill` 工具 / 调用方负责)。 */
|
|
309
315
|
userInvokedSkills?: string[];
|
|
310
316
|
/** [ref]② (core 1.314 工具面控制批): roster 真卸载清单(schema 不上 wire,manifest 收窄)。wire 工具名;
|
|
311
|
-
* tighten-only(core 继承不变量 union 进子 spawn)。数组-of-string fail-loud at submit。
|
|
317
|
+
* tighten-only(core 继承不变量 union 进子 spawn)。数组-of-string fail-loud at submit。
|
|
318
|
+
* 读成空的条目(退役拼法 `bash`、大小写孪生 `BASH`、任何工具名都不可能有的拼法、`*`)7.131.0 起受理口 400 `config.tool_face_control` 并点名现名
|
|
319
|
+
* (拒侧读成空 = 放行,不照跑);读得出的名字与真未知名(拼写合法、没有近似也没有退役对应)照收。 */
|
|
312
320
|
excludeTools?: string[];
|
|
313
321
|
/** S-1448(core 7.49.0 #1623,[ref]):引擎的**可用性选择器** —— 这一 run 只用列出的工具(wire 名、别名、`Tool(content)` 整只、
|
|
314
322
|
* `mcp__srv` / `mcp__srv__*` / 工具段通配;`"*"` = 本层不设名单;`[]` = 什么都不给)。宿主**原样透传**进 `TaskSpec.allowTools`:
|
|
315
|
-
* 不算补集、不过滤、不与 `settings.permissions.allow`(免问)
|
|
316
|
-
* 任何工具名都不可能有的拼法由引擎开跑前拒(`config.tool_face_control`,同步腿 400)
|
|
323
|
+
* 不算补集、不过滤、不与 `settings.permissions.allow`(免问)混词;真未知名(拼写合法、没有近似也没有退役对应:懒挂载的合法形)照跑(原样保留,今天什么都不准,之后挂上同名工具即生效;引擎告知运维一次),
|
|
324
|
+
* 任何工具名都不可能有的拼法由引擎开跑前拒(`config.tool_face_control`,同步腿 400);7.131.0 起受理口另拒近似名(大小写孪生 `BASH`:想放的什么也没放 = 静默收窄,句子点名现名);
|
|
325
|
+
* 放行侧的退役名(`bash` · `Task`)、内容形(`Agent(Explore)`)与文法读不出照旧受理、交引擎既有通告。
|
|
317
326
|
* `excludeTools` 是同一只运算的减项(deny 赢)。值形:字符串数组(受理门 400 `request.field_invalid`,与 `excludeTools` 同一只门);
|
|
318
327
|
* 装着的引擎不认该座 ⇒ 400 `request.field_invalid` 点名所需引擎版本(不静默丢)。无租户门(收窄)。 */
|
|
319
328
|
allowTools?: string[];
|
|
@@ -501,15 +510,19 @@ export interface TaskRequestBody {
|
|
|
501
510
|
* worker, not the exec env); IGNORED on multi-tenant deployments (can't make the shared worker run an arbitrary
|
|
502
511
|
* command). Validated + gated downstream; the center/config baseline wins on a name clash.
|
|
503
512
|
*
|
|
513
|
+
* 每个受理键一条律:新提交(fresh)腿上,条目的任一受理键(`name` / `transport` 及其子键 / `allowTools` / `toolAxes` / `toolFaces` / `contentOrigin` / `elicitation` / `source`)
|
|
514
|
+
* 在场却判不出形 ⇒ 同步 400 `request.field_invalid`,拒句点名 `mcpServers[i].<键路径>`、server 名与该键判官的原因句(不回显值),整份请求不收;键缺席恒合法。
|
|
515
|
+
* 耐久 resume 重放存量 body 的腿不拒(会砖死已 park 任务):坏硬键整条 server 不进 + 运维 warn,坏软键丢键、server 照挂。
|
|
516
|
+
*
|
|
504
517
|
* [ref](core 5.60.0):条目上可选的 `contentOrigin`(`"local"|"execution"|"external"`)= **内容
|
|
505
518
|
* 出处声明**,只影响记忆写治理(会话污染标记),对审批门/策略/角色表零作用。缺席 = pre-378 逐字节
|
|
506
519
|
* 现行为(判 external)。🔴 **新提交(fresh)腿上,词表外的值 ⇒ 同步 400 `request.field_invalid`** ——
|
|
507
520
|
* 静默丢会把一次信任边界声明变成探针。⚠️ **耐久 resume 腿上不拒**:那条腿重放的是存量 body,4xx 会把
|
|
508
521
|
* 一条 parked 任务永久砖死(折成 `409 resume_blocked_by_policy`),所以坏值在那里**降级**为「键缺席」+
|
|
509
|
-
* 一条 `mcp_content_class_dropped` warn(本仓三处 `opts.leg` 先例的同款姿势)
|
|
522
|
+
* 一条 `mcp_content_class_dropped` warn(本仓三处 `opts.leg` 先例的同款姿势)。重放腿上 server 本体
|
|
510
523
|
* 保留 —— 丢的只是声明。core 把这个键定性为**部署面**的键,本仓对它的 reject-or-strip 义务由既有的单用户闸
|
|
511
524
|
* **结构性**兑现:多租户腿整只 `mcpServers` 被忽略 ⇒ 租户够不着;单用户腿 = core 明文豁免的超管面
|
|
512
|
-
* (完整论证见 `task-mcp.ts` 的 `
|
|
525
|
+
* (完整论证见 `task-mcp.ts` 的 `assertRequestMcpEntries` 顶注)。部署面的正门是 center 下发的
|
|
513
526
|
* `CenterMcpServer.contentOrigin`。
|
|
514
527
|
*
|
|
515
528
|
* S-1515:`transport.kind:"http"` 的条目可选 `assertionHeader`(托管连接器断言头的**头名**,契约包 settings-schema ≥12.2.0 同键)。
|
package/dist/index.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export { RateLimiter, type RateDecision } from "./observability/rate-limit.js";
|
|
|
17
17
|
export { GiteaClient, GitHubClient, createRepoClient, GIT_API_KINDS, isGitApiKind, repoToolsFor, parseRepo, type GitApiKind, type RepoCoords, type RepoReadClient } from "./capabilities/repo-tools.js";
|
|
18
18
|
export { nowTool } from "./capabilities/builtin-tools.js";
|
|
19
19
|
export { loadSkills, skillsForScenario, type LoadedSkill } from "./capabilities/skills.js";
|
|
20
|
-
export { buildScenarios, selectScenario, type Scenario, type ScenarioBundle, type ScenarioContext, type ScenarioDeps, type ScenarioRequest, } from "./capabilities/scenarios.js";
|
|
20
|
+
export { buildScenarios, selectScenario, createScenarioGate, type Scenario, type ScenarioBundle, type ScenarioContext, type ScenarioDeps, type ScenarioGate, type ScenarioGateLeg, type ScenarioGateSources, type ScenarioRequest, } from "./capabilities/scenarios.js";
|
|
21
21
|
export { createTaskListLane, taskListKeyFor, MAX_TASK_LIST_SESSIONS, type TaskListLane, type TaskListLaneBackend, type TaskListLaneOpts } from "./boot/task-list-lane.js";
|
|
22
22
|
export { ensureTiDBTaskListSchema, ensurePgTaskListSchema } from "./plugins/task-list-store-sql.js";
|
|
23
23
|
export { createAuthorizer, principalFrom, HttpError, type AuthContext, type OwnerAwareSessionStore, } from "./security.js";
|
|
@@ -30,5 +30,6 @@ export { isModelKeyPoison, ModelKeyPoisonedError, type ModelKeyPoison, type Mode
|
|
|
30
30
|
export { CATALOG_ENTRY_HEADER_LANES, CATALOG_ENTRY_HEADER_NAMES, type CatalogEntryHeaderName } from "./model-entry-headers.js";
|
|
31
31
|
export { judgeModelRouteGap, type ModelRouteGapReading, type ModelRouteGapOptions } from "./model-route-gap.js";
|
|
32
32
|
export type { ModelRouteGap } from "./model-route-endpoint.js";
|
|
33
|
+
export { judgeServedModelRoute, type ServedModelRouteView, type ServedModelRouteOptions, type ModelRouteEndpointVerdict } from "./model-route-endpoint.js";
|
|
33
34
|
export type { UserInputLedgerStore, UserInputAcceptance, NewUserInputAcceptance, OpenScanCursor, OpenScanPage, SettleHow, WriterGoneEvidence } from "./user-input-ledger.js";
|
|
34
35
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ export { RateLimiter } from "./observability/rate-limit.js";
|
|
|
15
15
|
export { GiteaClient, GitHubClient, createRepoClient, GIT_API_KINDS, isGitApiKind, repoToolsFor, parseRepo } from "./capabilities/repo-tools.js";
|
|
16
16
|
export { nowTool } from "./capabilities/builtin-tools.js";
|
|
17
17
|
export { loadSkills, skillsForScenario } from "./capabilities/skills.js";
|
|
18
|
-
export { buildScenarios, selectScenario, } from "./capabilities/scenarios.js";
|
|
18
|
+
export { buildScenarios, selectScenario, createScenarioGate, } from "./capabilities/scenarios.js";
|
|
19
19
|
export { createTaskListLane, taskListKeyFor, MAX_TASK_LIST_SESSIONS } from "./boot/task-list-lane.js";
|
|
20
20
|
export { ensureTiDBTaskListSchema, ensurePgTaskListSchema } from "./plugins/task-list-store-sql.js";
|
|
21
21
|
export { createAuthorizer, principalFrom, HttpError, } from "./security.js";
|
|
@@ -26,4 +26,5 @@ export { createKeyResolver, resolveModelApiKey } from "./key-resolver.js";
|
|
|
26
26
|
export { isModelKeyPoison, ModelKeyPoisonedError } from "./key-resolver.js";
|
|
27
27
|
export { CATALOG_ENTRY_HEADER_LANES, CATALOG_ENTRY_HEADER_NAMES } from "./model-entry-headers.js";
|
|
28
28
|
export { judgeModelRouteGap } from "./model-route-gap.js";
|
|
29
|
+
export { judgeServedModelRoute } from "./model-route-endpoint.js";
|
|
29
30
|
//# sourceMappingURL=index.js.map
|
|
@@ -66,8 +66,8 @@ export declare function readMcpIdentityHeaders(transport: Record<string, unknown
|
|
|
66
66
|
};
|
|
67
67
|
/** 同设的拒句 —— 对契约包解析期那一句**逐字**(`server` = 条目名,调用方负责清洗后传入)。 */
|
|
68
68
|
export declare function describeMcpIdentityHeadersConflict(server: string): string;
|
|
69
|
-
/** `assertionHeader`
|
|
70
|
-
export declare function describeMcpAssertionHeaderNotAString(
|
|
69
|
+
/** `assertionHeader` 值不是字符串的**原因句**(不带条目名与键路径:点名由请求面统一的拒形 `entryFieldRefusal` 负责;值的形状只说种类,不回显值本身:它是一个调用方写的头名位置,可能是任何东西)。 */
|
|
70
|
+
export declare function describeMcpAssertionHeaderNotAString(value: unknown): string;
|
|
71
71
|
/** 这一条请求面条目是否声明了 `assertionHeader`(= 铸 core 规格时会被剥掉、需要说一行的那一类)。 */
|
|
72
72
|
export declare function declaresMcpAssertionHeader(spec: RequestMcpServerSpec): boolean;
|
|
73
73
|
/** 请求面条目 → 交给引擎的规格:剥掉 `assertionHeader`(引擎无座);其余逐字不动。声明过它的条目由调用方另说一行 info。 */
|
|
@@ -16,9 +16,9 @@ export function readMcpIdentityHeaders(transport) {
|
|
|
16
16
|
export function describeMcpIdentityHeadersConflict(server) {
|
|
17
17
|
return `mcp server "${server}" sets both principalHeader and assertionHeader — an http entry names ONE header for the identity it sends: keep principalHeader (the end-user principal) or assertionHeader (the connector assertion), not both`;
|
|
18
18
|
}
|
|
19
|
-
export function describeMcpAssertionHeaderNotAString(
|
|
20
|
-
const kind = value === null ? "null" : Array.isArray(value) ? "array" : typeof value
|
|
21
|
-
return `
|
|
19
|
+
export function describeMcpAssertionHeaderNotAString(value) {
|
|
20
|
+
const kind = value === null ? "null" : Array.isArray(value) ? "an array" : `a ${typeof value}`;
|
|
21
|
+
return `must be a string (the HTTP header NAME the connector assertion travels in) — got ${kind}; omit the key to send no assertion header`;
|
|
22
22
|
}
|
|
23
23
|
export function declaresMcpAssertionHeader(spec) {
|
|
24
24
|
return spec.transport.kind === "http" && spec.transport.assertionHeader !== undefined;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ModelEntryRefusalCode, type ModelPlaneRefusalCode, type ModelRefusalsSignal } from "@sema-agent/settings-schema";
|
|
2
2
|
import { type DroppedCatalogEntry } from "./web-admin-client/read-warnings.js";
|
|
3
|
+
import { type ModelTableLeg } from "./model-select.js";
|
|
3
4
|
/**
|
|
4
5
|
* 条目拒因码 → 本仓铸它的成因 —— **以码为键**(S-768):码闭集的属主是 settings-schema(`MODEL_ENTRY_REFUSAL_CODES`,6.2.0 起;此前本仓
|
|
5
6
|
* 自持一份同名码表,撞名已消灭、零别名)。那边加一个条目码 ⇒ 本表缺键 ⇒ 本仓 tsc 红(只拿 `satisfies` 约束码值证明的是「子集」,
|
|
@@ -126,13 +127,6 @@ export interface DefaultModelView {
|
|
|
126
127
|
readonly models: Readonly<Record<string, unknown>>;
|
|
127
128
|
readonly modelRefusals: Readonly<Record<string, ModelEntryRefusal>>;
|
|
128
129
|
}
|
|
129
|
-
/**
|
|
130
|
-
* 这张表是从哪条腿来的(闭集 —— 拒因句的出路按腿分:加腿不补 {@link defaultModelRefusal} 的臂 = 编译红)。
|
|
131
|
-
* · `env` —— 进程此刻服务的表**没有目录在管**(没配目录 / 目录迟到且无 LKG / 目录的模型面这一组被拒):`MODEL_ID` 是出路之一;
|
|
132
|
-
* · `catalog` —— 判的是一份**目录候选**(模型面 stage):目录一旦生效就整张换掉 env 表,`MODEL_ID` 救不了这份候选 ——
|
|
133
|
-
* 出路只在目录里(修条目 / 换绑)。opus 腿 ④(7.101.0 合并复审):此前两腿同一句,目录腿教「设 MODEL_ID 即可」是假话。
|
|
134
|
-
*/
|
|
135
|
-
export type ModelTableLeg = "env" | "catalog";
|
|
136
130
|
/**
|
|
137
131
|
* 🔴 S-705 —— **这张模型表有没有一只缺省模型**(`models.default`)。`undefined` = 有。
|
|
138
132
|
*
|
|
@@ -15,12 +15,15 @@
|
|
|
15
15
|
* (`MODEL_GATEWAY_FALLBACK_URLS`)是主网关的镜像,没有主就没有镜像 ⇒ 拒。
|
|
16
16
|
*
|
|
17
17
|
* ## 执法点(判据只在这里,执法点在调用处 —— 与 S-475 `gateway-url-userinfo` 同形)
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
18
|
+
* **判官只判「将要服务的那张表」,拒因句按视图说真话**(视图词 = {@link ModelTableLeg} 两词,进判据入参,不靠调用方拼句):
|
|
19
|
+
* · boot:装配第 2 段**目录应用之后**的跨域不变量区(`boot/stage-02-web-admin-pull.ts`,锚 `CROSS-INVARIANT:model-route-endpoint`)⇒ 拒启 ——
|
|
20
|
+
* 经 {@link judgeServedModelRoute}:目录被采纳 ⇒ 判目录表(目录视图),目录缺席 / 被整拒 / 没有启用条目 ⇒ 判 env 表(env 视图);
|
|
21
|
+
* 严格名册布防(`isRosterGateArmed`)时 env 表是占位、从不被派发 ⇒ 不判它(备用网关那一条与表无关,照判)。
|
|
22
|
+
* 不在 `loadConfig()`:那是目录应用**之前**,env 表还不知道会不会服务(目录一旦采纳就整张换掉它);
|
|
23
|
+
* · 包根导出的 `judgeModelRouteGap(env)`(`model-route-gap.ts`)问的是**env 腿**:「这份 env 交给引擎、没有目录时起不起得来」—— 同一只判据的 env 视图,
|
|
24
|
+
* 目录在场时起服以目录为准;
|
|
25
|
+
* · 热路径:`web-admin-client/apply-effective.ts` 的模型面 stage ⇒ 模型面组拒(opus 腿 O3:同候选其余组照落)、上一代模型面继续服务(目录视图);
|
|
26
|
+
* · run-local:配置源解析完之后按真要派发的表判(`run-local.ts`)—— 本入口没有名册门,不豁免占位;
|
|
24
27
|
* · 登记:`config-invariants.ts` 的 `model-route-endpoint` 条(表的「不许有表外不变量」职责);
|
|
25
28
|
* · 派发面:titler / hook 选模型(`hooks/hook-llm.ts`)与降级腿 / 启动摘要(`brain.ts`)求有效端点时调
|
|
26
29
|
* {@link openaiRouteBaseUrl},不再各写一个 `model.baseUrl || gatewayBaseUrl`。
|
|
@@ -34,6 +37,7 @@
|
|
|
34
37
|
* `brain.ts` / `hooks/hook-llm.ts` 都可以安全引它。
|
|
35
38
|
*/
|
|
36
39
|
import type { Model } from "@sema-agent/core";
|
|
40
|
+
import { type ModelTableLeg } from "./model-select.js";
|
|
37
41
|
/** 机读码(拼法随 `config.*` 家族,按**规则**命名不按键名):一条 openai 路由解析不到端点。 */
|
|
38
42
|
export declare const MODEL_ROUTE_ENDPOINT_MISSING_CODE = "config.model_route_endpoint_missing";
|
|
39
43
|
/** 同族第二码:备用网关设了、主网关没设。与上一码分开是因为出路不同(补主网关 / 撤备用 vs 补主网关 / 给条目 baseUrl)。 */
|
|
@@ -43,7 +47,7 @@ export declare const GATEWAY_FALLBACK_WITHOUT_PRIMARY_CODE = "config.gateway_fal
|
|
|
43
47
|
*
|
|
44
48
|
* 与上面的**码**是两根轴:码是组拒的机读词(`ModelPlaneRefusalCode`,settings-schema 属主),这里是**出路**:
|
|
45
49
|
* · `endpoint_missing` —— 缺的是网关端点:openai 兼容腿上的条目解析不到 base URL(或备用网关设了、主网关没设);
|
|
46
|
-
* 出路 = 设 `MODEL_GATEWAY_BASEURL
|
|
50
|
+
* 出路 = 设 `MODEL_GATEWAY_BASEURL`,或(目录视图)给条目自己的 `baseUrl` / (env 视图)挂 anthropic 路由、或不设 `MODEL_ID` 让目录缺省服务;
|
|
47
51
|
* · `lane_credential_missing` —— 缺的是**车道凭据**:条目的 provider 词是 `anthropic`(用户选的是 anthropic 车道),
|
|
48
52
|
* 却因 `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN` 都没有而没装上 anthropic 路由、落进了 openai 腿,那条腿又没有端点;
|
|
49
53
|
* 出路 = 补那两把钥匙之一(设网关是另一条出路,拒因句一并给);
|
|
@@ -75,8 +79,11 @@ export type OpenaiRouteEndpoint = {
|
|
|
75
79
|
readonly ok: false;
|
|
76
80
|
readonly refusal: string;
|
|
77
81
|
};
|
|
78
|
-
/**
|
|
79
|
-
|
|
82
|
+
/**
|
|
83
|
+
* 单条 openai 路由的有效端点 / 拒因(拒因与部署级判据是**同一只**铸点 {@link routeEndpointMissingRefusal};`leg` = 这条路由所在的表的视图词)。
|
|
84
|
+
* `route` = 这条路由的条目(只读 `baseUrl` 与 `provider`);条目都没有(模型名解不出)⇒ 传 `undefined`,按「无自有 baseUrl、非 anthropic 词」判。
|
|
85
|
+
*/
|
|
86
|
+
export declare function resolveOpenaiRouteEndpoint(routeName: string, route: Pick<Model, "baseUrl" | "provider"> | undefined, gatewayBaseUrl: string | undefined, leg: ModelTableLeg): OpenaiRouteEndpoint;
|
|
80
87
|
/** 部署级判据读的切面(`ServiceConfig` 与 `CrossInvariantView` 都结构满足)。 */
|
|
81
88
|
export interface ModelRouteEndpointView {
|
|
82
89
|
readonly gatewayBaseUrl?: string;
|
|
@@ -96,7 +103,7 @@ export interface ModelRouteEndpointView {
|
|
|
96
103
|
* `default` 键是目录里另一条的别名(env 腿 `{default: m, [m.id]: m}`、center 腿 `models.default = models[picked]`),
|
|
97
104
|
* 与别的键指同一只对象时不重复点名;只有它一个键时(手搓夹具)照样点名 `default`。
|
|
98
105
|
*/
|
|
99
|
-
export declare function modelRouteEndpointRefusal(view: ModelRouteEndpointView): string | undefined;
|
|
106
|
+
export declare function modelRouteEndpointRefusal(view: ModelRouteEndpointView, leg: ModelTableLeg): string | undefined;
|
|
100
107
|
/** S-768:部署级判决的结构化形 —— 码与拒因句由**同一次**判决给出(一个不变量 id 两个码,组拒码在判决那刻当数据带出,不从句子里抠)。 */
|
|
101
108
|
export interface ModelRouteEndpointVerdict {
|
|
102
109
|
readonly code: typeof GATEWAY_FALLBACK_WITHOUT_PRIMARY_CODE | typeof MODEL_ROUTE_ENDPOINT_MISSING_CODE;
|
|
@@ -107,6 +114,39 @@ export interface ModelRouteEndpointVerdict {
|
|
|
107
114
|
/**
|
|
108
115
|
* **判据属主**(S-768 起的结构化形;{@link modelRouteEndpointRefusal} 是它的句子投影):`undefined` = 通过。判序即 {@link modelRouteEndpointRefusal}
|
|
109
116
|
* 头注所列 —— ① 备用无主先说,② 逐条目解析不到端点全部点名。加一枝 = 在这里同时给出码与句(返回型强制两者同在)。
|
|
117
|
+
* `leg` = `view.models` 是哪一张表({@link ModelTableLeg}):判据本身两视图同一把,**句子**按视图铸(出路在 env 表与目录表上不是同一组)。
|
|
118
|
+
*/
|
|
119
|
+
export declare function judgeModelRouteEndpoint(view: ModelRouteEndpointView, leg: ModelTableLeg): ModelRouteEndpointVerdict | undefined;
|
|
120
|
+
/** {@link judgeServedModelRoute} 读的切面:路由判据的切面 + 「服务的是哪张表」的事实(`ServiceConfig` 结构满足)。 */
|
|
121
|
+
export interface ServedModelRouteView extends ModelRouteEndpointView {
|
|
122
|
+
readonly model: object;
|
|
123
|
+
readonly envModel: object;
|
|
124
|
+
}
|
|
125
|
+
/** {@link judgeServedModelRoute} 的旁路入参。 */
|
|
126
|
+
export interface ServedModelRouteOptions {
|
|
127
|
+
/**
|
|
128
|
+
* env 表是不是**占位**(只在服务的是 env 表时有意义)。严格名册布防(`isRosterGateArmed`:远程 registry ∧ 非 dryRun ∧ `CONFIG_REQUIRE_ROSTER=true`)=
|
|
129
|
+
* 运维声明 env `MODEL_ID` 是占位:名册落地前计费提交 503、落地后整表换装 ⇒ env 表从不被派发,不在「能派发到的」射程里(备用网关那一条与表无关,照判)。
|
|
130
|
+
* 布防是中心接入的事实,配置读不出 ⇒ 由调用方给。没有名册门的入口(run-local)传 `false`。
|
|
131
|
+
*/
|
|
132
|
+
readonly envTableIsPlaceholder: boolean;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* **起服段的路由判官:只判「将要服务的那张表」**。`undefined` = 通过。
|
|
136
|
+
*
|
|
137
|
+
* 服务的是哪张表由 {@link servedModelTableLeg} 读出来,判据与句子随它走(同一只属主判据 {@link judgeModelRouteEndpoint},零第二判官):
|
|
138
|
+
* · 目录被采纳 ⇒ 判目录表(目录视图;模型面 stage 已对同一张表判过同一只判据,这里是装配终点上的复核);
|
|
139
|
+
* · 目录缺席 / 被整拒 / 没有启用条目 ⇒ 判 env 表(env 视图);占位豁免见 {@link ServedModelRouteOptions.envTableIsPlaceholder}。
|
|
140
|
+
*
|
|
141
|
+
* 必须在**目录应用之后**调(装配第 2 段 / run-local 配置源解析完):在那之前没有「将要服务的表」可判 ——
|
|
142
|
+
* 对 env 表提前拒启,会把「条目写进目录、再设 `MODEL_ID`」的部署无谓拒掉(目录一旦采纳,引擎从不读 env `MODEL_ID`)。
|
|
143
|
+
*/
|
|
144
|
+
export declare function judgeServedModelRoute(config: ServedModelRouteView, opts: ServedModelRouteOptions): ModelRouteEndpointVerdict | undefined;
|
|
145
|
+
/**
|
|
146
|
+
* **没有目录要应用的直接装配**(开发脚本 / 基准 / 嵌入方的纯 env 形:`loadConfig()` → `createBrain` → `Runner`)的拒启口:服务的表就是 `loadConfig()` 铸的 env 表,
|
|
147
|
+
* 判决 ⇒ 抛(`Error.message` = 判决的 `reason` 原样)。`loadConfig()` 不判路由 —— 没有这一步,缺端点的 env 行会一路跑到第一次模型调用才失败(基准里是十次失败调用、
|
|
148
|
+
* 全零统计、退出 0)。装配第 2 段与 `run-local` 要锚、要在目录应用之后判,调的是 {@link judgeServedModelRoute};本函数只是它的抛出形。
|
|
149
|
+
* 源码类钉(`test/served-model-route.test.ts` ⑨):`src/` 与 `scripts/` 里每个调 `loadConfig()` 的入口,要么在同文件里判过路由,要么是登记在册的例外。
|
|
110
150
|
*/
|
|
111
|
-
export declare function
|
|
151
|
+
export declare function assertServedModelRoute(config: ServedModelRouteView, opts: ServedModelRouteOptions): void;
|
|
112
152
|
//# sourceMappingURL=model-route-endpoint.d.ts.map
|