@sema-agent/server 7.128.0 → 7.129.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 +118 -0
- package/USAGE.md +89 -18
- package/dist/approval-card.d.ts +53 -26
- package/dist/approval-card.js +40 -11
- package/dist/approval-crash-convergence.js +4 -4
- package/dist/bake-runner/main.js +2 -1
- package/dist/boot/memory-consolidation.d.ts +46 -16
- package/dist/boot/memory-consolidation.js +15 -8
- package/dist/boot/side-query-lane.d.ts +7 -20
- package/dist/boot/side-query-lane.js +0 -24
- package/dist/boot/stage-01-config.js +3 -0
- package/dist/boot/stage-06-runners.js +1 -1
- package/dist/boot/stores.js +2 -2
- package/dist/boot/web-admin.js +17 -10
- package/dist/boot/webfetch-summarize-lane.d.ts +10 -7
- package/dist/boot/webfetch-summarize-lane.js +2 -3
- package/dist/brain.js +6 -9
- package/dist/capabilities/builtin-tools.d.ts +2 -2
- package/dist/capabilities/engine-capabilities.d.ts +14 -0
- package/dist/capabilities/engine-capabilities.js +12 -0
- package/dist/capability-facts.d.ts +3 -1
- package/dist/capability-facts.js +1 -0
- package/dist/config-types.d.ts +4 -3
- package/dist/egress-redirect.d.ts +12 -0
- package/dist/egress-redirect.js +7 -0
- package/dist/entry-mode-clamp-notice.d.ts +30 -0
- package/dist/entry-mode-clamp-notice.js +13 -0
- package/dist/fleet/fleet-bus.d.ts +7 -0
- package/dist/fleet/fleet-bus.js +1 -0
- package/dist/hooks/hook-llm.js +6 -2
- package/dist/host-lsp-manager.d.ts +3 -3
- package/dist/http/admission.js +5 -2
- package/dist/http/resume-legs.js +46 -27
- package/dist/http/route-ctx.d.ts +1 -1
- package/dist/http/routes/approvals-assistant.js +6 -4
- package/dist/http/routes/capabilities.js +1 -0
- package/dist/http/routes/session-sync.js +3 -4
- package/dist/http/routes/sessions.js +4 -17
- package/dist/http/send.d.ts +1 -1
- package/dist/http/send.js +4 -3
- package/dist/http/wire-types.d.ts +3 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/observability/fail-open.d.ts +7 -3
- package/dist/observability/fail-open.js +7 -3
- package/dist/observability/secret-env-scrub.d.ts +37 -3
- package/dist/observability/secret-env-scrub.js +2 -2
- package/dist/parked-decide.js +5 -2
- package/dist/plugins/memory-embedder.js +5 -0
- package/dist/plugins/one-time-migrations.d.ts +180 -9
- package/dist/plugins/one-time-migrations.js +315 -3
- package/dist/plugins/pg-pool.js +3 -1
- package/dist/plugins/remote-env-host.d.ts +17 -20
- package/dist/plugins/remote-env-host.js +13 -25
- package/dist/plugins/session-policy-store-sql.d.ts +16 -4
- package/dist/plugins/session-policy-store-sql.js +17 -20
- package/dist/plugins/session-rules-column.d.ts +16 -0
- package/dist/plugins/session-rules-column.js +6 -0
- package/dist/plugins/tidb-pool.js +4 -1
- package/dist/read-face-posture.d.ts +2 -1
- package/dist/request-value-closure.js +1 -1
- package/dist/retired-card-words.d.ts +17 -0
- package/dist/retired-card-words.js +49 -0
- package/dist/rule-entry-list.js +2 -0
- package/dist/run-local.js +10 -3
- package/dist/server-secret-env.d.ts +77 -85
- package/dist/server-secret-env.js +40 -26
- package/dist/session-policy-wire.d.ts +61 -124
- package/dist/session-policy-wire.js +59 -121
- package/dist/session-sync.d.ts +8 -6
- package/dist/session-sync.js +6 -5
- package/dist/spec-fields.d.ts +3 -2
- package/dist/standing-answer-grant.d.ts +17 -9
- package/dist/standing-answer-grant.js +11 -8
- package/dist/task-settings.d.ts +6 -1
- package/dist/task-settings.js +1 -1
- package/dist/tool-approval.d.ts +14 -7
- package/dist/tool-approval.js +23 -18
- package/dist/trace/core-keyset-guard.d.ts +3 -3
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +1 -0
- package/dist/trace/project.d.ts +7 -4
- package/dist/trace/project.js +6 -75
- package/dist/trace/tool-output-media.d.ts +2 -1
- package/dist/trace/tool-output-media.js +3 -25
- package/dist/web-admin-client/apply-effective.js +28 -11
- package/dist/web-admin-client/facade.d.ts +1 -1
- package/dist/web-admin-client/facade.js +1 -1
- package/dist/web-admin-client/read-face.d.ts +73 -20
- package/dist/web-admin-client/read-face.js +76 -36
- package/dist/web-admin-client/skills-mcp.d.ts +1 -1
- package/dist/web-admin-client/skills-mcp.js +1 -1
- package/dist/web-admin-client/types.d.ts +3 -3
- package/package.json +3 -3
- package/dist/mcp-tool-axes.d.ts +0 -62
- package/dist/mcp-tool-axes.js +0 -47
|
@@ -328,5 +328,7 @@ export declare function workspaceCache(c: Pick<ServiceConfig, "remoteExec">): Fa
|
|
|
328
328
|
}>;
|
|
329
329
|
/** 自唤醒调度器挂在宿主 env 上:`SCHEDULER_ENABLED` ∧ 单用户 ∧ host 车道。装配(`boot/execution-env.ts`)与能力位同调。 */
|
|
330
330
|
export declare function hostScheduler(c: Pick<ServiceConfig, "schedulerEnabled" | "requirePrincipal" | "remoteExec">): Fact<"scheduler_disabled" | "multi_tenant_deployment" | "not_host_lane">;
|
|
331
|
-
|
|
331
|
+
/** 引擎能力词的事实(core `ENGINE_CAPABILITIES` 每词一只谓词,值 = 本进程的事实;`/v1/capabilities` 以顶层布尔键展开它们)—— 住在
|
|
332
|
+
* `capabilities/engine-capabilities.ts`,这里只是把它纳入「能力位只投影事实」的同一张读面,静态门认 `facts.engineCapabilityBits()` 这一只展开。 */
|
|
333
|
+
export { engineCapabilityBits } from "./capabilities/engine-capabilities.js";
|
|
332
334
|
//# sourceMappingURL=capability-facts.d.ts.map
|
package/dist/capability-facts.js
CHANGED
package/dist/config-types.d.ts
CHANGED
|
@@ -471,8 +471,9 @@ export interface ServiceConfigFlat {
|
|
|
471
471
|
* 由 boot(config.ts)与热路径(apply-effective)同一只判据保证。 */
|
|
472
472
|
gatewayBaseUrl?: string;
|
|
473
473
|
/** `MODEL_API_KEY` —— **网关的**凭据,只与网关根配对([ref])。S-623:与 `gatewayBaseUrl` 同进同出 ——
|
|
474
|
-
* 没有网关就不装载(否则 brain
|
|
475
|
-
* openai
|
|
474
|
+
* 没有网关就不装载(否则 brain 的声明根为空:core 7.51.0 起判 `route.credential_mismatch`,每条自带 baseUrl、没有
|
|
475
|
+
* per-model 凭据的 openai 条目被拒;7.51.0 前是 `unpinned`,key 随请求发往那些条目),由 `model_gateway_key_unpaired`
|
|
476
|
+
* 启动通知说出来。S-1561:brain 在构造时读它一次(core 收据律),本仓没有运行期写者 ⇒ 换值 = 重启。 */
|
|
476
477
|
gatewayApiKey?: string;
|
|
477
478
|
/** Secondary openai-compatible gateways for cross-gateway FAILOVER (same protocol + same model id).
|
|
478
479
|
* Tried in order after the primary when a gateway fails before streaming any content. */
|
|
@@ -1759,7 +1760,7 @@ export interface ServiceConfigFlat {
|
|
|
1759
1760
|
* its own meaning (the sandbox lane's opt-in above) and is unaffected. */
|
|
1760
1761
|
lspHostEnabled: boolean;
|
|
1761
1762
|
/** S-699 —— host-lane language-server env **named allowlist** (`LSP_ENV_ALLOW=NAME,NAME`). The language server's env is
|
|
1762
|
-
* `
|
|
1763
|
+
* the engine's `inheritedHostEnv("scrub")` (shape rules + this deployment's declared own credentials); each name here is put back **verbatim** from the service's own env on top of that
|
|
1763
1764
|
* (`allowlistedEnv`, the same "named keys pass" predicate as the host shell's `inheritEnv:[names]`) — the deployer
|
|
1764
1765
|
* explicitly accepts handing that value to code the language server runs. Unnamed secret-shaped keys stay stripped.
|
|
1765
1766
|
* `[]` = nothing put back (the default: unset / empty string). A malformed list (empty segment / not an env-var name /
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 本服务**自己**发往配置端点的出站请求(hook 直连模型腿 `hooks/hook-llm.ts`、记忆 embedder `plugins/memory-embedder.ts`)的
|
|
3
|
+
* 重定向律 —— 与 core 7.51.0(#1531)一方 brain 的请求同一条:请求以 `redirect: "manual"` 发出,端点回重定向 ⇒ 拒;
|
|
4
|
+
* 不跟、不重试、拒句不点名目标。
|
|
5
|
+
*
|
|
6
|
+
* 为什么:请求只去被判过的那个根。传输层(undici)跟随 POST 307 / 308 时把请求体(用户内容 / 记忆正文)原样送到
|
|
7
|
+
* `Location`,跨 origin 只剥 `authorization` —— anthropic 形的 `x-api-key` 随行(`test/hook-llm.test.ts` 的 G1 格在
|
|
8
|
+
* 7.128.x 上实测送达重定向目标)。判据与拒句与 core 的 brain 请求门同形(`opaqueredirect` 或 3xx)。
|
|
9
|
+
*/
|
|
10
|
+
/** 端点回的是重定向 ⇒ 拒句(调用方在前面冠上自己的面名);否则 `undefined`。命中时丢弃响应体(不读、不留连接)。 */
|
|
11
|
+
export declare function egressRedirectRefusal(res: Response, fix: string): string | undefined;
|
|
12
|
+
//# sourceMappingURL=egress-redirect.d.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function egressRedirectRefusal(res, fix) {
|
|
2
|
+
if (res.type !== "opaqueredirect" && !(res.status >= 300 && res.status < 400))
|
|
3
|
+
return undefined;
|
|
4
|
+
void res.body?.cancel().catch(() => undefined);
|
|
5
|
+
return `endpoint answered with a redirect${res.status > 0 ? ` (HTTP ${res.status})` : ""}; it was not followed and no credential was sent anywhere else — ${fix}`;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=egress-redirect.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S-1306 —— **入口地板钳制**通告(`engine_notice` 码 `permission.entry_mode_clamped`)的唯一铸点(core 7.51.0 #1527 入册:受众 user、
|
|
3
|
+
* 铸者 `NOTICE_MINTER` = `"server"`,引擎从不铸它)。
|
|
4
|
+
*
|
|
5
|
+
* ## 这件事为什么归本仓
|
|
6
|
+
* 运维 `AUTONOMY=plan` 的入口地板在**受理门**把调用方请求的模式词折回地板(`task-settings.ts` `stampEntryPermissionMode`,底下是
|
|
7
|
+
* `runtime-governance.ts` `entryPermissionMode` → core `clampPermissionMode`)—— 折叠发生在 run 到达引擎**之前**,所以引擎自己的
|
|
8
|
+
* `config.permission_mode_unavailable`(它只说它自己做的折叠)看不见这一折。修前 run 在另一个模式下起步,人一个字都看不到。
|
|
9
|
+
*
|
|
10
|
+
* ## 铸点(唯一)
|
|
11
|
+
* {@link buildEntryModeClampNotice} 是本码**唯一**的铸点;调用点只有一处:`http/admission.ts` 的 `prepareSpec`(提交入口,盖章之后、装配
|
|
12
|
+
* 之后 —— 那时会话 id 才定)。一次提交一次 ⇒ 一条 run 至多一帧(core 码册的去重单位「once per run」)。读的是盖章那一次求值的读数
|
|
13
|
+
* (`EntryPermissionMode`),不另判一遍「折没折」。续跑腿不铸:它们重放的是已盖过章的存储体,折叠只发生过一次。
|
|
14
|
+
* 装配期铸、腿开口那一拍投(`PreparedTaskSubmission.engineNotices` → `registerEngineNoticeLeg` 的 `pending`)—— 与请求腿 MCP 丢弃同一条投递路,
|
|
15
|
+
* 白名单 / 归属 / 消毒 / 隔离一个不绕。
|
|
16
|
+
*
|
|
17
|
+
* ## detail
|
|
18
|
+
* `{ requested, effective, ceiling, sessionId }` —— `requested` = 被折的那个词(体的生效词:顶层 `permissionMode` 或
|
|
19
|
+
* `settings.permissions.defaultMode`)、`effective` = run 起步的词、`ceiling` = 折它的地板,三个都是 core 的模式闭集词;`sessionId` = 归属键。
|
|
20
|
+
* 🔴 **无 `runId`**:钳制发生在受理门,引擎的 run 身份要到 prepare 才铸,这一刻不存在(core 码册那一行写了 `runId`;本仓不编造,
|
|
21
|
+
* 归属看它落在哪条 run 的流 / 账本上 —— 与 `instructions.source_changed` / `session.records_skipped` 同形)。
|
|
22
|
+
* 什么时候不铸:没有地板;没声明词(取地板本身不算改词,`clamped` 为假);请求的词不比地板松;会话 id 缺席(无处可投,宁缺不串)。
|
|
23
|
+
*/
|
|
24
|
+
import type { EngineNotice, EngineNoticeCode } from "@sema-agent/core";
|
|
25
|
+
import type { EntryPermissionMode } from "./runtime-governance.js";
|
|
26
|
+
/** 码常量型 = core 码册(#1527 入册:受众 user、铸者 `"server"`)。 */
|
|
27
|
+
export declare const ENTRY_MODE_CLAMPED: EngineNoticeCode;
|
|
28
|
+
/** 一次受理门盖章的读数 → 至多一条通告(读法见文件头)。 */
|
|
29
|
+
export declare function buildEntryModeClampNotice(entry: EntryPermissionMode, sessionId: string | undefined): EngineNotice | undefined;
|
|
30
|
+
//# sourceMappingURL=entry-mode-clamp-notice.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const ENTRY_MODE_CLAMPED = "permission.entry_mode_clamped";
|
|
2
|
+
export function buildEntryModeClampNotice(entry, sessionId) {
|
|
3
|
+
const { requested, effective, floor, clamped } = entry;
|
|
4
|
+
if (!clamped || requested === undefined || floor === undefined || effective === undefined || sessionId === undefined || sessionId.length === 0)
|
|
5
|
+
return undefined;
|
|
6
|
+
return {
|
|
7
|
+
code: ENTRY_MODE_CLAMPED,
|
|
8
|
+
message: `This run starts in permission mode "${effective}", not the requested "${requested}": ` +
|
|
9
|
+
`the deployment's entry floor ("${floor}") clamps every submitted run's mode to it.`,
|
|
10
|
+
detail: { requested, effective, ceiling: floor, sessionId },
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=entry-mode-clamp-notice.js.map
|
|
@@ -420,6 +420,13 @@ export interface BgNotification {
|
|
|
420
420
|
seq?: number;
|
|
421
421
|
status: "completed" | "failed" | "killed";
|
|
422
422
|
summary?: string;
|
|
423
|
+
/** S-1296(core 7.51.0 #1482 / #1528):the child's REPORT — the same string the completion frame the parent's model reads carries
|
|
424
|
+
* (core cuts it once at the frame's bound, redacting first), so a host that wakes an idle conversation reads it here instead of a
|
|
425
|
+
* second, differently-cut copy. UNTRUSTED child text ⇒ `redactSecrets` at this wire boundary, exactly like `summary` on this frame
|
|
426
|
+
* and `result` on the `task_notification` frame (one reading: the server redacts at its own boundary whatever upstream did).
|
|
427
|
+
* Notification-only (the task ROW is state and never carries it). ABSENT when the frame carries none (a killed / failed leg with no
|
|
428
|
+
* report, a withheld hand-back). */
|
|
429
|
+
result?: string;
|
|
423
430
|
/** Background lifetime scope ([ref]), NOT the tenant scope. */
|
|
424
431
|
scope: "task" | "session";
|
|
425
432
|
parentTaskId?: string;
|
package/dist/fleet/fleet-bus.js
CHANGED
|
@@ -560,6 +560,7 @@ export function fleetBackgroundChildPublisher(bus, log) {
|
|
|
560
560
|
...(typeof e.seq === "number" ? { seq: e.seq } : {}),
|
|
561
561
|
status: e.status ?? "failed",
|
|
562
562
|
...(e.summary ? { summary: redactSecrets(e.summary) } : {}),
|
|
563
|
+
...(e.result !== undefined ? { result: redactSecrets(e.result) } : {}),
|
|
563
564
|
scope: (m.sessionScoped || e.sessionScoped) ? "session" : "task",
|
|
564
565
|
...(m.parentTaskId ? { parentTaskId: m.parentTaskId } : m.resolvedParentId ? { parentTaskId: m.resolvedParentId } : {}),
|
|
565
566
|
...(e.completionId ? { completionId: e.completionId } : {}),
|
package/dist/hooks/hook-llm.js
CHANGED
|
@@ -2,6 +2,7 @@ import { adjudicateModelRoute, joinRequestUrl, resolveRouteCredential, routeKeyO
|
|
|
2
2
|
import { matchCatalogModel, refusedCatalogEntry } from "../model-select.js";
|
|
3
3
|
import { modelUnavailableMessage } from "../model-entry-refusal.js";
|
|
4
4
|
import { resolveOpenaiRouteEndpoint } from "../model-route-endpoint.js";
|
|
5
|
+
import { egressRedirectRefusal } from "../egress-redirect.js";
|
|
5
6
|
import { buildDisplaySafeRefusal, redactSecrets } from "../trace/redact.js";
|
|
6
7
|
export function buildHookFailureText(raw) {
|
|
7
8
|
if (raw === "")
|
|
@@ -70,7 +71,7 @@ export function createHookLlm(deps) {
|
|
|
70
71
|
buildDisplaySafeRefusal(routeRefusalText(verdict), [pick.baseUrl, declaredRoot ?? ""]),
|
|
71
72
|
};
|
|
72
73
|
}
|
|
73
|
-
if (
|
|
74
|
+
if (verdict.posture === "keyless" && routeKeyOf(pick.baseUrl) !== routeKeyOf(declaredRoot)) {
|
|
74
75
|
return {
|
|
75
76
|
ok: false,
|
|
76
77
|
error: `hook model "${pick.catalogRef}" points at ${pick.baseUrl}, which this deployment did not declare as its own endpoint, ` +
|
|
@@ -131,7 +132,10 @@ export function createHookLlm(deps) {
|
|
|
131
132
|
headers = { "content-type": "application/json", ...(hookKey ? { authorization: `Bearer ${hookKey}` } : {}) };
|
|
132
133
|
body = JSON.stringify({ model: pick.providerModelId, ...(maxOutputTokens !== undefined ? { max_tokens: maxOutputTokens } : {}), messages: [...(system ? [{ role: "system", content: system }] : []), { role: "user", content: prompt }], stream: false });
|
|
133
134
|
}
|
|
134
|
-
const r = await fetchImpl(url, { method: "POST", headers, body, signal: ac.signal });
|
|
135
|
+
const r = await fetchImpl(url, { method: "POST", headers, body, signal: ac.signal, redirect: "manual" });
|
|
136
|
+
const redirected = egressRedirectRefusal(r, "declare the final endpoint as this route's base URL");
|
|
137
|
+
if (redirected !== undefined)
|
|
138
|
+
return { ok: false, error: `hook llm ${redirected}` };
|
|
135
139
|
if (!r.ok)
|
|
136
140
|
return { ok: false, error: `hook llm HTTP ${r.status}` };
|
|
137
141
|
let text;
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* `process.env`,脑钥 / 仓库令牌 / 本仓自己的凭据键全在里面,而语言服务器会执行仓里的代码(构建脚本、插件、
|
|
7
7
|
* 工作区配置)。7.30.1 起 core 默认改成 `scrubSecretEnv(process.env)`(按键名**形状**剥),并开了
|
|
8
8
|
* `NodeLspManagerOptions.env` 一格让宿主自己交 env(core 帖 [ref] ④;core 确认本仓的正确读法 = 交 host 车道**同一只**
|
|
9
|
-
* `
|
|
9
|
+
* 本仓唯一组合 `hostSubprocessEnv` 的结果 —— 引擎 `inheritedHostEnv("scrub")` ∪ 点名放回)。
|
|
10
10
|
*
|
|
11
11
|
* ## 规则(一句)
|
|
12
|
-
* 语言服务器子进程的 env = `
|
|
12
|
+
* 语言服务器子进程的 env = 引擎 `inheritedHostEnv("scrub")`(形状剥 ∪ 本进程起服声明的自有凭据名)∪ 部署方点名放回的键(S-699,见下节;缺省空名单)——
|
|
13
13
|
* 剥除与模型可驱动的 host shell(`inheritEnv:"scrub"`)
|
|
14
14
|
* 和 project-memory 的 git 子进程**同一只函数**:目录 `type:"secret"` 键按名剥 ∘ core 形状规则按形剥。只交 core 的默认
|
|
15
15
|
* 会漏掉形状规则认不出的本仓凭据键(当时是 `OTEL_EXPORTER_OTLP_HEADERS`,core 7.33.7 #1180 起 core 按名剥它;今天只剩名单型旋钮三只),那正是 S-684 当初的病形。
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* ## 点名放行(S-699,`LSP_ENV_ALLOW`)
|
|
24
24
|
* 剥除会连语言服务器自己的真实依赖一起剥掉(`.npmrc` 的 `${NPM_TOKEN}` 插值、rust-analyzer `cargo metadata` 私有 registry 的
|
|
25
25
|
* `CARGO_REGISTRIES_<名>_TOKEN` —— 形状规则按名判,分不出「给语言服务器用的令牌」与「编排器的令牌」)。出路 = 部署方点名:
|
|
26
|
-
* env = `
|
|
26
|
+
* env = 引擎 `inheritedHostEnv("scrub")` 之上把 `config.lspEnvAllow` 点到的键经 `allowlistedEnv`(与沙箱三腿
|
|
27
27
|
* `inheritEnv:[names]` 同一只谓词)从原 env 原样放回;没点到的照旧剥 —— 7.103.0 起这件组合只有一只:{@link hostSubprocessEnv}
|
|
28
28
|
* (`server-secret-env.ts`,bake-runner 的 `BAKE_ENV_ALLOW` 调同一只)。名单由 `config.ts` 解析(形错拒启;
|
|
29
29
|
* `REQUIRE_PRINCIPAL=true` 下落空名单并 warn 一次),本函数只读 `config.lspEnvAllow`,不在这里再判一次姿态。
|
package/dist/http/admission.js
CHANGED
|
@@ -3,6 +3,7 @@ import { isAtMentionable, matchCatalogModel, refusedCatalogEntry, selectableCata
|
|
|
3
3
|
import { modelUnavailableMessage } from "../model-entry-refusal.js";
|
|
4
4
|
import { HttpError, sessionIdShapeIssue } from "../security.js";
|
|
5
5
|
import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS, PERMISSION_MODE_WORDS, parseTaskSettings, settingsPermissionBucketIssue, skillListingBudgetFractionOk, skillListingMaxDescOk, stampEntryPermissionMode, taskHooksHonored, taskSettingsKeyIssue } from "../task-settings.js";
|
|
6
|
+
import { buildEntryModeClampNotice } from "../entry-mode-clamp-notice.js";
|
|
6
7
|
import { autonomyModeFloor } from "../runtime-governance.js";
|
|
7
8
|
import { taskBodyKeyIssue } from "../request-key-closure.js";
|
|
8
9
|
import { FILE_HISTORY_DISABLED_NO_RESTORE, fileHistoryRestoreConflict, inheritEnvFieldIssue, singleMemberOffFieldIssue } from "../request-value-closure.js";
|
|
@@ -629,7 +630,7 @@ export function createAdmission(ctx) {
|
|
|
629
630
|
sendError(res, 400, "request.precondition_unmet", "cascade requested but no ladder configured — set MODEL_CASCADE_LADDER (catalog model names, cheapest→strongest)");
|
|
630
631
|
return null;
|
|
631
632
|
}
|
|
632
|
-
const admitted = stampEntryPermissionMode(body, autonomyModeFloor(deps.config.autonomy));
|
|
633
|
+
const { body: admitted, entry: entryMode } = stampEntryPermissionMode(body, autonomyModeFloor(deps.config.autonomy));
|
|
633
634
|
try {
|
|
634
635
|
const auth = deps.authorize ? await deps.authorize({ req, body: admitted }) : undefined;
|
|
635
636
|
const engineNotices = [];
|
|
@@ -644,7 +645,9 @@ export function createAdmission(ctx) {
|
|
|
644
645
|
});
|
|
645
646
|
const model = modelIdOf(spec.model);
|
|
646
647
|
const acceptance = createRunAcceptance({ sessionTitler: deps.sessionTitler }, { ...(spec.sessionId ? { sessionId: spec.sessionId } : {}), objective: spec.objective, ...(model !== undefined ? { model } : {}) });
|
|
647
|
-
|
|
648
|
+
const clampNotice = buildEntryModeClampNotice(entryMode, spec.sessionId);
|
|
649
|
+
const pendingNotices = clampNotice !== undefined ? [clampNotice, ...engineNotices] : engineNotices;
|
|
650
|
+
return { spec, runner: runnerFor(spec), auth, verify, cascade, jobId: admitted.jobId, body: admitted, acceptance, ...(bindingIntent === undefined ? {} : { bindingIntent }), ...(pendingNotices.length > 0 ? { engineNotices: pendingNotices } : {}) };
|
|
648
651
|
}
|
|
649
652
|
catch (err) {
|
|
650
653
|
if (err instanceof HttpError) {
|
package/dist/http/resume-legs.js
CHANGED
|
@@ -49,9 +49,8 @@ import { buildCheckpointVersionKeys, resumeBlockedByPolicyAnswer, storedBodyRepl
|
|
|
49
49
|
import { storedBodyReplayRefusal } from "./stored-body-replay.js";
|
|
50
50
|
import { httpErrorCode } from "./send.js";
|
|
51
51
|
import { createRunAcceptance } from "../run-acceptance.js";
|
|
52
|
-
function
|
|
53
|
-
|
|
54
|
-
return code === "resume.env_failed" || code === "resume.tool_unavailable" || code === "resume.tool_contract_mismatch";
|
|
52
|
+
function legMayHaveReopenedCheckpoint(r) {
|
|
53
|
+
return r.terminal.kind === "failed" && nestedPauseOf(r.terminal) === undefined;
|
|
55
54
|
}
|
|
56
55
|
function nestedParkStatusOf(r) {
|
|
57
56
|
const paused = nestedPauseOf(r.terminal);
|
|
@@ -476,6 +475,7 @@ export function createResumeLegs(ctx) {
|
|
|
476
475
|
async function driveResumeLeg(args, onAccepted) {
|
|
477
476
|
const resumeLegStartedAt = Date.now();
|
|
478
477
|
const { runner: legRunner, token, sessionId, principal, fleetScope, taskConfig, resumeObjective, outcome, verifyRounds, resumeCtx } = args;
|
|
478
|
+
const reparkStatus = outcome.gate === "plan_review" ? "needs_review" : "suspended";
|
|
479
479
|
const commitResumeCtx = () => deps.checkpointStore.putCtx(resumeCtx.sessionId, { ...resumeCtx.ctx, writerVersion: serviceVersion() }).catch((e) => {
|
|
480
480
|
deps.metrics?.inc("resume_ctx_not_persisted_total");
|
|
481
481
|
deps.logger?.warn?.("resume_ctx_not_persisted", { sessionId: resumeCtx.sessionId, gate: outcome.gate, error: errorText(e), note: "this continuation runs with the body it was given; the durable rebuild input still holds the PREVIOUS body, so the NEXT continuation of this session rebuilds from that one (never wider than this leg's)" });
|
|
@@ -562,6 +562,32 @@ export function createResumeLegs(ctx) {
|
|
|
562
562
|
return true;
|
|
563
563
|
}
|
|
564
564
|
};
|
|
565
|
+
const settleCardForCancel = async () => {
|
|
566
|
+
const cs2 = deps.checkpointStore;
|
|
567
|
+
if (!cs2)
|
|
568
|
+
return;
|
|
569
|
+
try {
|
|
570
|
+
const live = await cs2.get(token);
|
|
571
|
+
if (live?.status === "pending" && !(await cs2.expire(token, live.scope))) {
|
|
572
|
+
deps.logger?.warn?.("resume_cancel_checkpoint_settle_lost", { taskId, sessionId });
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
catch (cancelSettleErr) {
|
|
576
|
+
deps.logger?.warn?.("resume_cancel_checkpoint_settle_failed", {
|
|
577
|
+
taskId, sessionId, err: cancelSettleErr instanceof Error ? cancelSettleErr.message : String(cancelSettleErr),
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
const reopenedAfterLeg = async (leg, cancelledAtDone) => {
|
|
582
|
+
if (freshRunClaimed || !legMayHaveReopenedCheckpoint(leg))
|
|
583
|
+
return false;
|
|
584
|
+
const pending = cancelledAtDone ? false : await confirmCheckpointReopened();
|
|
585
|
+
if (cancelledAtDone || cancelCtrl.cause() !== undefined) {
|
|
586
|
+
await settleCardForCancel();
|
|
587
|
+
return false;
|
|
588
|
+
}
|
|
589
|
+
return pending;
|
|
590
|
+
};
|
|
565
591
|
let resumeHeartbeat;
|
|
566
592
|
let resumeManifestLease = 0;
|
|
567
593
|
let resumeStreamRef;
|
|
@@ -685,7 +711,7 @@ export function createResumeLegs(ctx) {
|
|
|
685
711
|
const vrNested = nestedPauseOf(vr.terminal);
|
|
686
712
|
const reParkStatus = nestedParkStatusOf(vr);
|
|
687
713
|
const reSuspended = vrNested !== undefined;
|
|
688
|
-
const vrReopened =
|
|
714
|
+
const vrReopened = await reopenedAfterLeg(vr, vrCause !== undefined);
|
|
689
715
|
if (vrReopened)
|
|
690
716
|
reopenConfirmedPark = true;
|
|
691
717
|
if (taskId && deps.runStore) {
|
|
@@ -696,7 +722,7 @@ export function createResumeLegs(ctx) {
|
|
|
696
722
|
if (reSuspended || vrReopened) {
|
|
697
723
|
await appendModelUsageDelta(ap, deps.modelUsage, taskId);
|
|
698
724
|
const vrParkCallId = vrNested !== undefined ? await parkToolCallId({ terminal: vrNested }, deps.checkpointStore) : undefined;
|
|
699
|
-
await ap(reParkStatus ??
|
|
725
|
+
await ap(reParkStatus ?? reparkStatus, { gate: vrNested?.gate ?? null, ...(vrParkCallId !== undefined ? { toolCallId: vrParkCallId } : {}), ...(vrReopened ? { reopened: failureCodeOf(vr.terminal) ?? null } : {}) });
|
|
700
726
|
}
|
|
701
727
|
else {
|
|
702
728
|
safe = await attachModelUsage(safe, { append: ap, getEvents: (id, a) => rs2.getEvents(id, a), modelUsage: deps.modelUsage, taskId });
|
|
@@ -819,8 +845,12 @@ export function createResumeLegs(ctx) {
|
|
|
819
845
|
resumeStreamRef = stream;
|
|
820
846
|
if (taskId)
|
|
821
847
|
steerableRuns.set(taskId, stream);
|
|
822
|
-
if (!taskId || !rs)
|
|
823
|
-
|
|
848
|
+
if (!taskId || !rs) {
|
|
849
|
+
const rNoRow = await stream.result();
|
|
850
|
+
if (await reopenedAfterLeg(rNoRow, cancelCtrl.cause() !== undefined))
|
|
851
|
+
reopenConfirmedPark = true;
|
|
852
|
+
return sealResultForExit(rNoRow);
|
|
853
|
+
}
|
|
824
854
|
onAccepted?.(acceptedResumingReceipt(taskId, sessionId));
|
|
825
855
|
let causeAtDone;
|
|
826
856
|
for await (const ev of stream) {
|
|
@@ -951,10 +981,10 @@ export function createResumeLegs(ctx) {
|
|
|
951
981
|
await appendModelUsageDelta(append, deps.modelUsage, taskId);
|
|
952
982
|
return r;
|
|
953
983
|
}
|
|
954
|
-
if (
|
|
984
|
+
if (await reopenedAfterLeg(r, causeAtDone !== undefined)) {
|
|
955
985
|
reopenConfirmedPark = true;
|
|
956
986
|
try {
|
|
957
|
-
await append(
|
|
987
|
+
await append(reparkStatus, { gate: null, reopened: failureCodeOf(r.terminal) ?? null });
|
|
958
988
|
await appendModelUsageDelta(append, deps.modelUsage, taskId);
|
|
959
989
|
}
|
|
960
990
|
catch {
|
|
@@ -1000,20 +1030,7 @@ export function createResumeLegs(ctx) {
|
|
|
1000
1030
|
const refusalCause = e.code === "checkpoint.invalid_outcome" ? decisionRefusalCauseOf(e, outcome.gate) : undefined;
|
|
1001
1031
|
const disposition = resumeRefusalDisposition(e);
|
|
1002
1032
|
if (disposition === "cancelled") {
|
|
1003
|
-
|
|
1004
|
-
if (cs2) {
|
|
1005
|
-
try {
|
|
1006
|
-
const live = await cs2.get(token);
|
|
1007
|
-
if (live?.status === "pending" && !(await cs2.expire(token, live.scope))) {
|
|
1008
|
-
deps.logger?.warn?.("resume_cancel_checkpoint_settle_lost", { taskId, sessionId });
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
catch (cancelSettleErr) {
|
|
1012
|
-
deps.logger?.warn?.("resume_cancel_checkpoint_settle_failed", {
|
|
1013
|
-
taskId, sessionId, err: cancelSettleErr instanceof Error ? cancelSettleErr.message : String(cancelSettleErr),
|
|
1014
|
-
});
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1033
|
+
await settleCardForCancel();
|
|
1017
1034
|
if (claimedRow && taskId && deps.runStore) {
|
|
1018
1035
|
const handleCause = cancelCtrl.cause();
|
|
1019
1036
|
if (handleCause === undefined)
|
|
@@ -1035,8 +1052,8 @@ export function createResumeLegs(ctx) {
|
|
|
1035
1052
|
settleFleet("failed");
|
|
1036
1053
|
}
|
|
1037
1054
|
else if (retriable) {
|
|
1038
|
-
await (
|
|
1039
|
-
settleFleet(
|
|
1055
|
+
await (reparkStatus === "needs_review" ? deps.runStore.setNeedsReview(taskId, null) : deps.runStore.setSuspended(taskId, null)).catch(() => undefined);
|
|
1056
|
+
settleFleet(reparkStatus);
|
|
1040
1057
|
}
|
|
1041
1058
|
else {
|
|
1042
1059
|
await deps.runStore.setTerminal(taskId, "failed", null, e.message).catch(() => undefined);
|
|
@@ -1099,14 +1116,16 @@ export function createResumeLegs(ctx) {
|
|
|
1099
1116
|
const sealed = sealResultForExit(result);
|
|
1100
1117
|
const resumePlane = outwardPlane(sealed.terminal);
|
|
1101
1118
|
if (taskId && deps.runStore) {
|
|
1102
|
-
if (
|
|
1119
|
+
if (checkpointReopened && reparkStatus === "needs_review")
|
|
1120
|
+
await deps.runStore.setNeedsReview(taskId, null);
|
|
1121
|
+
else if (resumePlane.status === "suspended" || checkpointReopened)
|
|
1103
1122
|
await deps.runStore.setSuspended(taskId, sealed.terminal.kind === "paused" ? sealed : null);
|
|
1104
1123
|
else if (resumePlane.status === "needs_review")
|
|
1105
1124
|
await deps.runStore.setNeedsReview(taskId, sealed);
|
|
1106
1125
|
else
|
|
1107
1126
|
await deps.runStore.setTerminal(taskId, resumePlane.status, sealed, resumePlane.errorMessage ?? null);
|
|
1108
1127
|
}
|
|
1109
|
-
settleFleet(checkpointReopened ?
|
|
1128
|
+
settleFleet(checkpointReopened ? reparkStatus : resumePlane.status, fleetRunResiduals(sealed));
|
|
1110
1129
|
return {
|
|
1111
1130
|
status: 200,
|
|
1112
1131
|
decisionCommitted: true,
|
package/dist/http/route-ctx.d.ts
CHANGED
|
@@ -128,7 +128,7 @@ export interface PreparedTaskSubmission {
|
|
|
128
128
|
cascade?: boolean;
|
|
129
129
|
jobId?: string;
|
|
130
130
|
body: TaskRequestBody;
|
|
131
|
-
/** L-167:**装配期**铸下的、这条会话的用户面 `EngineNotice`(
|
|
131
|
+
/** L-167:**装配期**铸下的、这条会话的用户面 `EngineNotice`(今天两族:请求腿 MCP 注入被丢弃;入口地板钳制 `permission.entry_mode_clamped`)。
|
|
132
132
|
* 装配跑在这条腿的通告口开张之前,所以铸点交不出去 —— 由腿在 `registerEngineNoticeLeg` 那一拍
|
|
133
133
|
* 经 `pending` 一起投(投递单点仍是那一只注册助手,不新开第二条机制)。缺席 = 本次装配没铸下任何通告。 */
|
|
134
134
|
engineNotices?: readonly EngineNotice[];
|
|
@@ -18,7 +18,7 @@ import { sendJson, sendError, sseHeaders, SSE_MAX_STREAM_MS, SSE_HEARTBEAT_IDLE_
|
|
|
18
18
|
import { gatedPrincipal, explicitOperatorOk, isOperator, sendPrincipalRefusal } from "../principal-gate.js";
|
|
19
19
|
import { governanceOriginOf } from "../../runtime-governance.js";
|
|
20
20
|
import { deploymentListAsks } from "../../approval.js";
|
|
21
|
-
import {
|
|
21
|
+
import { callGrantBindingOf, questionGrantBindingOf } from "../../standing-answer-grant.js";
|
|
22
22
|
import { sendResumeOutcome } from "../route-ctx.js";
|
|
23
23
|
import { assertNever } from "../route-ctx.js";
|
|
24
24
|
import * as facts from "../../capability-facts.js";
|
|
@@ -321,7 +321,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
321
321
|
return;
|
|
322
322
|
}
|
|
323
323
|
const rows = await deps.approvalExemptionStore.list(exSessionId);
|
|
324
|
-
sendJson(res, 200, { exemptions: rows.filter((r) =>
|
|
324
|
+
sendJson(res, 200, { exemptions: rows.filter((r) => callGrantBindingOf({ toolName: r.toolName }).kind === "none") });
|
|
325
325
|
return;
|
|
326
326
|
}
|
|
327
327
|
if (exToolName === undefined) {
|
|
@@ -360,6 +360,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
360
360
|
...(s.boundGrantDigest !== undefined ? { boundGrantDigest: s.boundGrantDigest } : {}),
|
|
361
361
|
...(s.boundGrants !== undefined ? { boundGrants: s.boundGrants } : {}),
|
|
362
362
|
...(s.classifierNoRuling !== undefined ? { classifierNoRuling: s.classifierNoRuling } : {}),
|
|
363
|
+
...(s.boundGrantAbsence !== undefined ? { boundGrantAbsence: s.boundGrantAbsence } : {}),
|
|
363
364
|
objective,
|
|
364
365
|
input,
|
|
365
366
|
};
|
|
@@ -840,13 +841,14 @@ function projectPendingForWire(row, deployment) {
|
|
|
840
841
|
const governance = deployment;
|
|
841
842
|
const redacted = redactPendingDisclosures(row);
|
|
842
843
|
const governanceForced = governanceOriginOf({ riskDescriptor: row.riskDescriptor ?? undefined }, governance);
|
|
844
|
+
const deploymentListQuestion = carriesDeploymentListQuestion(row.toolName !== null && deployment !== undefined && deploymentListAsks(deployment, { toolName: row.toolName }), row.classes);
|
|
843
845
|
const rememberRefused = !standingAnswerDurablyRecordable({
|
|
844
846
|
governanceForced: governanceForced === true,
|
|
845
847
|
requiresRealApproval: row.requiresRealApproval === true,
|
|
846
848
|
mandated: row.mandated === true || row.ruleOffersAbsence === "mandated",
|
|
847
|
-
grantBinding:
|
|
849
|
+
grantBinding: questionGrantBindingOf(row, deploymentListQuestion),
|
|
848
850
|
classes: row.classes,
|
|
849
|
-
deploymentListQuestion
|
|
851
|
+
deploymentListQuestion,
|
|
850
852
|
});
|
|
851
853
|
return { ...redacted, ...(governanceForced ? { governanceForced } : {}), ...(rememberRefused ? { rememberRefused: true } : {}) };
|
|
852
854
|
}
|
|
@@ -73,6 +73,7 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
73
73
|
const rulesLaneForCaller = facts.ruleLaneForCaller(deps, gatedPrincipal(req, deps.config)).available;
|
|
74
74
|
sendJson(res, 200, gateCompositeMembers({
|
|
75
75
|
...deps.capabilities,
|
|
76
|
+
...facts.engineCapabilityBits(),
|
|
76
77
|
asyncRuns: facts.durableRuns(deps).available,
|
|
77
78
|
artifacts: facts.durableRuns(deps).available,
|
|
78
79
|
approvals: facts.checkpointPark(deps).available,
|
|
@@ -14,7 +14,7 @@ const SYNC_NDJSON_LINE_MAX_BYTES = 8 * 1024 * 1024;
|
|
|
14
14
|
const SYNC_IMPORT_BATCH = 500;
|
|
15
15
|
import { SHA256_HEX_RE } from "../../digest-form.js";
|
|
16
16
|
import { assertNever, SESSION_NOT_FOUND_MESSAGE } from "../route-ctx.js";
|
|
17
|
-
import { readSessionRulesRecords, describeSessionRulesRecordProblem
|
|
17
|
+
import { readSessionRulesRecords, describeSessionRulesRecordProblem } from "../../session-policy-wire.js";
|
|
18
18
|
import { readSessionPinned, readSessionTitle } from "../../session-row.js";
|
|
19
19
|
export function createSessionSyncLocal() {
|
|
20
20
|
const pendingStagings = new Map();
|
|
@@ -232,9 +232,8 @@ export async function handleSessionSync(req, res, match, ctx) {
|
|
|
232
232
|
}
|
|
233
233
|
const policyRead = readSessionRulesRecords(body.policy);
|
|
234
234
|
if (policyRead.problem !== undefined) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
sendError(res, 400, "rules.legacy_tool_name", `policy: ${describeSessionRulesRecordProblem(policyRead.problem)}`, { ...legacy });
|
|
235
|
+
if (policyRead.problem.kind === "rules") {
|
|
236
|
+
sendSessionPolicyError(res, policyRead.problem.refusal, `policy: [${policyRead.problem.index}].rules: `);
|
|
238
237
|
return;
|
|
239
238
|
}
|
|
240
239
|
sendError(res, 400, "request.field_invalid", `policy: ${describeSessionRulesRecordProblem(policyRead.problem)}`);
|
|
@@ -7,7 +7,7 @@ import { recordFailOpen } from "../../observability/fail-open.js";
|
|
|
7
7
|
import { MCP_DIAL_DEADLINE_MS, raceDeadline } from "../mcp-dial-deadline.js";
|
|
8
8
|
import { sentMemoryComplianceFailure } from "../memory-compliance-failure-reply.js";
|
|
9
9
|
import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS } from "../../task-settings.js";
|
|
10
|
-
import { readWritableSessionPermissionRules
|
|
10
|
+
import { readWritableSessionPermissionRules } from "../../session-policy-wire.js";
|
|
11
11
|
import { sendJson, sendError, sendSessionError, sendSessionPolicyError, sseHeaders } from "../send.js";
|
|
12
12
|
import { gatedPrincipal, explicitOperatorOk, sessionOwnerReadable, sendPrincipalRefusal, sendUnauthorized } from "../principal-gate.js";
|
|
13
13
|
import { assertNever, SESSION_NOT_FOUND_MESSAGE } from "../route-ctx.js";
|
|
@@ -819,22 +819,9 @@ export async function handleSessions(req, res, match, ctx) {
|
|
|
819
819
|
return;
|
|
820
820
|
}
|
|
821
821
|
const read = readWritableSessionPermissionRules(body.rules);
|
|
822
|
-
if (read.
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
sendError(res, 400, "request.body_shape", describeSessionPermissionRulesProblem(read.problem));
|
|
826
|
-
return;
|
|
827
|
-
case "field-not-rule-list":
|
|
828
|
-
sendError(res, 400, "request.field_invalid", describeSessionPermissionRulesProblem(read.problem));
|
|
829
|
-
return;
|
|
830
|
-
case "legacy-tool-name":
|
|
831
|
-
sendError(res, 400, "rules.legacy_tool_name", describeSessionPermissionRulesProblem(read.problem), { ...read.problem.refusal });
|
|
832
|
-
return;
|
|
833
|
-
default: {
|
|
834
|
-
const unhandled = read.problem;
|
|
835
|
-
throw new Error(`unhandled SessionPermissionRules problem: ${JSON.stringify(unhandled)}`);
|
|
836
|
-
}
|
|
837
|
-
}
|
|
822
|
+
if (read.refusal !== undefined) {
|
|
823
|
+
sendSessionPolicyError(res, read.refusal);
|
|
824
|
+
return;
|
|
838
825
|
}
|
|
839
826
|
if (body.expectedRev !== undefined && (typeof body.expectedRev !== "number" || !Number.isInteger(body.expectedRev) || body.expectedRev < 0)) {
|
|
840
827
|
sendError(res, 400, "request.field_invalid", "expectedRev must be a non-negative integer");
|
package/dist/http/send.d.ts
CHANGED
|
@@ -84,7 +84,7 @@ export declare function httpErrorCode(status: number, code?: string): string;
|
|
|
84
84
|
* 域外通用臂 ⇒ `500 internal.error "internal error"`:码丢,连带**修复路径**也丢(同一行 `PUT` 却答
|
|
85
85
|
* `500 corrupt` 并点名那一列)。一只腿之后,新站点只会忘记调它,不会再各自发明一份映射。
|
|
86
86
|
*/
|
|
87
|
-
export declare function sendSessionPolicyError(res: ServerResponse, e: unknown): boolean;
|
|
87
|
+
export declare function sendSessionPolicyError(res: ServerResponse, e: unknown, sentencePrefix?: string): boolean;
|
|
88
88
|
/**
|
|
89
89
|
* S-612 —— core `SessionError` → 应答,经 {@link sessionErrorHttp} 那**一只**映射(`src/session-error-wire.ts`)。
|
|
90
90
|
* 回 `true` = 已应答,调用点 `return`;`false` = 不是 `SessionError`,或这个词在这一面上是登记过的留白(站点的既有
|
package/dist/http/send.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SessionError, SessionPolicyError } from "@sema-agent/core";
|
|
2
|
-
import { sessionPolicyErrorHttp } from "../session-policy-wire.js";
|
|
2
|
+
import { sessionPolicyErrorFacts, sessionPolicyErrorHttp, sessionPolicyErrorSentence } from "../session-policy-wire.js";
|
|
3
3
|
import { sessionErrorHttp } from "../session-error-wire.js";
|
|
4
4
|
import { redactErrorMessage } from "../observability/run-terminal.js";
|
|
5
5
|
export const SSE_RESPONSE_MARK = Symbol("sema.http.sse-response");
|
|
@@ -48,11 +48,12 @@ export function httpErrorCode(status, code) {
|
|
|
48
48
|
default: return status >= 500 ? "internal.error" : "request.rejected";
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
-
export function sendSessionPolicyError(res, e) {
|
|
51
|
+
export function sendSessionPolicyError(res, e, sentencePrefix = "") {
|
|
52
52
|
if (!(e instanceof SessionPolicyError))
|
|
53
53
|
return false;
|
|
54
54
|
const h = sessionPolicyErrorHttp(e.code);
|
|
55
|
-
|
|
55
|
+
const facts = sessionPolicyErrorFacts(e);
|
|
56
|
+
sendError(res, h.status, h.errorCode, redactErrorMessage(`${sentencePrefix}${sessionPolicyErrorSentence(e)}`) ?? "session policy error", facts === undefined ? undefined : { ...facts });
|
|
56
57
|
return true;
|
|
57
58
|
}
|
|
58
59
|
export function sendSessionError(res, e, face) {
|
|
@@ -312,7 +312,8 @@ export interface TaskRequestBody {
|
|
|
312
312
|
excludeTools?: string[];
|
|
313
313
|
/** S-1448(core 7.49.0 #1623,[ref]):引擎的**可用性选择器** —— 这一 run 只用列出的工具(wire 名、别名、`Tool(content)` 整只、
|
|
314
314
|
* `mcp__srv` / `mcp__srv__*` / 工具段通配;`"*"` = 本层不设名单;`[]` = 什么都不给)。宿主**原样透传**进 `TaskSpec.allowTools`:
|
|
315
|
-
* 不算补集、不过滤、不与 `settings.permissions.allow`(免问)
|
|
315
|
+
* 不算补集、不过滤、不与 `settings.permissions.allow`(免问)混词;没有任何工具答应的名字照跑(原样保留,今天什么都不准,之后挂上同名工具即生效;引擎告知运维一次),
|
|
316
|
+
* 任何工具名都不可能有的拼法由引擎开跑前拒(`config.tool_face_control`,同步腿 400)。
|
|
316
317
|
* `excludeTools` 是同一只运算的减项(deny 赢)。值形:字符串数组(受理门 400 `request.field_invalid`,与 `excludeTools` 同一只门);
|
|
317
318
|
* 装着的引擎不认该座 ⇒ 400 `request.field_invalid` 点名所需引擎版本(不静默丢)。无租户门(收窄)。 */
|
|
318
319
|
allowTools?: string[];
|
|
@@ -415,7 +416,7 @@ export interface TaskRequestBody {
|
|
|
415
416
|
/**
|
|
416
417
|
* S-895(7.106.0,core [ref] [ref] ②)—— 模型驱动的 host shell **继承多少用户环境**,由调用方按请求声明。
|
|
417
418
|
* 闭集两词(词表外 ⇒ 400 `request.field_invalid`,两腿同判,判据属主 `request-value-closure.ts` 的 `inheritEnvFieldIssue`):
|
|
418
|
-
* · `"all"` ——
|
|
419
|
+
* · `"all"` —— 整份用户环境**减本服务自有凭据名**(配置目录 secret 名 / 名单型旋钮名 / 模型条目 `apiKeyEnv`,起服向引擎声明一次,判在引擎;7.129.0 起,CC 交互形);只在**单用户**部署兑现,射程 = 写容纳面那一只
|
|
419
420
|
* `hostPostureDeclarationsHonored`(`hosted-posture.ts`)⇒ 托管部署(`REQUIRE_PRINCIPAL=true`)响亮拒
|
|
420
421
|
* 422 `request.host_posture_refused`;
|
|
421
422
|
* · `"scrub"` —— 缺省清洗(剥凭据形变量),**任何车道、任何部署**都受理(它就是缺省;7.106.0 合并复审 X7)。
|
package/dist/index.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export { createAuthorizer, principalFrom, HttpError, type AuthContext, type Owne
|
|
|
24
24
|
export { memoryScopeFor, memoryEngineBackendFor } from "./memory-scope.js";
|
|
25
25
|
export type { TaskRequestBody } from "./http/wire-types.js";
|
|
26
26
|
export { createHttpServer, type ServiceDeps, type RequestAuth, type FlatServiceDeps, type ServiceDepGroups, type ServiceCoreDeps, type ServiceStoreDeps, type ServiceCoordinatorDeps, type ServiceSeamDeps, type ServiceObservabilityDeps, type ServiceGovernanceDeps, type ServiceDeploymentDeps, type ServiceKnobDeps, } from "./http/server.js";
|
|
27
|
-
export { createSideQueryLane,
|
|
27
|
+
export { createSideQueryLane, type KeyResolver, type SideQueryLane, type SideQueryLaneCtx } from "./boot/side-query-lane.js";
|
|
28
28
|
export { createKeyResolver, resolveModelApiKey, type ModelKeyRef } from "./key-resolver.js";
|
|
29
29
|
export { isModelKeyPoison, ModelKeyPoisonedError, type ModelKeyPoison, type ModelKeyPoisonReason } from "./key-resolver.js";
|
|
30
30
|
export { CATALOG_ENTRY_HEADER_LANES, CATALOG_ENTRY_HEADER_NAMES, type CatalogEntryHeaderName } from "./model-entry-headers.js";
|
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ export { ensureTiDBTaskListSchema, ensurePgTaskListSchema } from "./plugins/task
|
|
|
21
21
|
export { createAuthorizer, principalFrom, HttpError, } from "./security.js";
|
|
22
22
|
export { memoryScopeFor, memoryEngineBackendFor } from "./memory-scope.js";
|
|
23
23
|
export { createHttpServer, } from "./http/server.js";
|
|
24
|
-
export { createSideQueryLane
|
|
24
|
+
export { createSideQueryLane } from "./boot/side-query-lane.js";
|
|
25
25
|
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";
|