@sema-agent/server 7.105.0 → 7.106.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 +82 -0
- package/USAGE.md +134 -42
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +2 -0
- package/deploy/sema-up/cluster-up.sh +15 -2
- package/deploy/sema-up/kube-up.sh +8 -3
- package/deploy/sema-up/lib/flagshape.sh +29 -0
- package/deploy/sema-up/lib/parse-machines.py +4 -1
- package/deploy/sema-up/sema-up.sh +11 -9
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +3 -1
- package/dist/approval-card.d.ts +1 -1
- package/dist/approval.d.ts +6 -1
- package/dist/approval.js +4 -4
- package/dist/auth-bridge.d.ts +14 -0
- package/dist/auth-bridge.js +7 -0
- package/dist/auto-mode-face.d.ts +49 -40
- package/dist/auto-mode-face.js +6 -68
- package/dist/bench/s1/live-deps.d.ts +9 -2
- package/dist/bench/s1/live-deps.js +13 -8
- package/dist/boot/budget-tracing.js +3 -2
- package/dist/boot/config-center.d.ts +4 -0
- package/dist/boot/config-center.js +95 -37
- package/dist/boot/coordinators.d.ts +1 -0
- package/dist/boot/coordinators.js +3 -1
- package/dist/boot/device-lane.d.ts +2 -1
- package/dist/boot/execution-env.d.ts +11 -0
- package/dist/boot/execution-env.js +30 -6
- package/dist/boot/execution-lane.js +2 -3
- package/dist/boot/leader.d.ts +2 -1
- package/dist/boot/leader.js +2 -0
- package/dist/boot/parked-revive-gate.d.ts +10 -7
- package/dist/boot/parked-revive-gate.js +4 -11
- package/dist/boot/resolve-spec.d.ts +1 -4
- package/dist/boot/resolve-spec.js +44 -21
- package/dist/boot/runner-deps.d.ts +5 -8
- package/dist/boot/runner-deps.js +3 -9
- package/dist/boot/side-query-lane.d.ts +2 -2
- package/dist/boot/stage-03-store-backend.d.ts +1 -1
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.d.ts +2 -1
- package/dist/boot/stage-05-execution-env.js +2 -2
- package/dist/boot/stage-06-runners.d.ts +3 -2
- package/dist/boot/stage-06-runners.js +2 -5
- package/dist/boot/stage-07-capability-layer.d.ts +2 -2
- package/dist/boot/stage-07-capability-layer.js +1 -2
- package/dist/boot/stage-08-reapers.d.ts +2 -2
- package/dist/boot/stage-09-leader.d.ts +2 -2
- package/dist/boot/stage-09-leader.js +2 -2
- package/dist/boot/stage-10-http-server.d.ts +2 -2
- package/dist/boot/stage-10-http-server.js +11 -7
- package/dist/boot/stores.d.ts +1 -1
- package/dist/boot/stores.js +6 -4
- package/dist/brain.d.ts +24 -2
- package/dist/brain.js +29 -10
- package/dist/budget.js +3 -0
- package/dist/capabilities/center-wiring.d.ts +64 -0
- package/dist/capabilities/center-wiring.js +24 -0
- package/dist/capabilities/composite-members.d.ts +89 -0
- package/dist/capabilities/composite-members.js +20 -0
- package/dist/capabilities/execution-lane.d.ts +3 -0
- package/dist/capabilities/execution-lane.js +5 -2
- package/dist/config-catalog.d.ts +30 -5
- package/dist/config-catalog.js +75 -26
- package/dist/config-center/apply-effective.d.ts +1 -1
- package/dist/config-center/apply-effective.js +7 -8
- package/dist/config-center/apply-ledger.d.ts +17 -0
- package/dist/config-center/apply-ledger.js +5 -0
- package/dist/config-center/center-request.d.ts +17 -0
- package/dist/config-center/center-request.js +4 -0
- package/dist/config-center/pull-state.d.ts +29 -0
- package/dist/config-center/pull-state.js +2 -0
- package/dist/config-types.d.ts +41 -14
- package/dist/config-types.js +1 -0
- package/dist/config.d.ts +35 -0
- package/dist/config.js +69 -16
- package/dist/cross-session-settings.d.ts +1 -1
- package/dist/deployment-governance.d.ts +12 -1
- package/dist/deployment-governance.js +2 -0
- package/dist/execution-lane-caps.d.ts +30 -2
- package/dist/execution-lane-caps.js +12 -0
- package/dist/fleet-client.d.ts +18 -13
- package/dist/fleet-client.js +32 -22
- package/dist/fleet-lease.d.ts +72 -47
- package/dist/fleet-lease.js +66 -14
- package/dist/governance-http-status.d.ts +0 -1
- package/dist/governance-http-status.js +0 -1
- package/dist/hosted-posture.d.ts +6 -17
- package/dist/hosted-posture.js +11 -6
- package/dist/http/admission.js +14 -9
- package/dist/http/approval-sweeps.js +13 -1
- package/dist/http/dispatch.js +1 -1
- package/dist/http/durable-run-leg.js +2 -0
- package/dist/http/request-guards.d.ts +25 -0
- package/dist/http/request-guards.js +25 -5
- package/dist/http/resume-legs.js +19 -11
- package/dist/http/route-ctx.d.ts +9 -0
- package/dist/http/routes/approvals-assistant.js +0 -1
- package/dist/http/routes/capabilities.js +12 -11
- package/dist/http/routes/config-catalog.js +2 -1
- package/dist/http/routes/diagnostics.js +2 -0
- package/dist/http/routes/tasks.js +4 -1
- package/dist/http/server.d.ts +20 -4
- package/dist/http/session-inherit-env.d.ts +37 -0
- package/dist/http/session-inherit-env.js +6 -0
- package/dist/http/wire-types.d.ts +19 -28
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/key-resolver.d.ts +74 -19
- package/dist/key-resolver.js +65 -7
- package/dist/leader/leader.d.ts +6 -0
- package/dist/leader/leader.js +2 -1
- package/dist/leader/repair-wire.js +2 -1
- package/dist/leader/wire.d.ts +9 -1
- package/dist/leader/wire.js +10 -6
- package/dist/observability/fail-open.d.ts +5 -1
- package/dist/observability/fail-open.js +5 -1
- package/dist/observability/metrics.js +2 -0
- package/dist/observability/permission-rule-events.d.ts +1 -1
- package/dist/observability/permission-rule-events.js +1 -1
- package/dist/observability/run-terminal.d.ts +38 -1
- package/dist/observability/run-terminal.js +2 -5
- package/dist/observability/secret-env-scrub.js +1 -1
- package/dist/observability/tool-trace.d.ts +2 -0
- package/dist/operator-ask.d.ts +29 -1
- package/dist/operator-ask.js +6 -0
- package/dist/parked-decide.d.ts +14 -1
- package/dist/parked-decide.js +8 -1
- package/dist/plugins/session-policy-store-sql.d.ts +38 -7
- package/dist/plugins/session-policy-store-sql.js +64 -22
- package/dist/plugins/sql-driver.d.ts +1 -1
- package/dist/plugins/store-backend.d.ts +12 -3
- package/dist/plugins/store-backend.js +16 -5
- package/dist/plugins/tidb-pool.js +1 -1
- package/dist/read-face-posture.d.ts +4 -4
- package/dist/request-key-closure.js +2 -2
- package/dist/request-value-closure.d.ts +17 -5
- package/dist/request-value-closure.js +12 -0
- package/dist/router/route-orchestration.d.ts +11 -0
- package/dist/router/route-orchestration.js +7 -6
- package/dist/run-local.js +4 -2
- package/dist/runs.d.ts +2 -1
- package/dist/runs.js +4 -4
- package/dist/runtime-caps-resolver.d.ts +4 -10
- package/dist/runtime-governance.d.ts +8 -11
- package/dist/runtime-governance.js +10 -41
- package/dist/sealed-key.d.ts +0 -40
- package/dist/sealed-key.js +0 -20
- package/dist/server-secret-env.d.ts +11 -12
- package/dist/session-policy-wire.d.ts +5 -3
- package/dist/session-titler.d.ts +11 -0
- package/dist/session-titler.js +6 -2
- package/dist/sql-engine-posture.d.ts +2 -2
- package/dist/store-posture.d.ts +53 -0
- package/dist/store-posture.js +32 -0
- package/dist/task-settings.d.ts +34 -45
- package/dist/task-settings.js +46 -25
- package/dist/tool-approval.d.ts +51 -22
- package/dist/tool-approval.js +44 -8
- package/dist/tool-declaration-http-status.d.ts +12 -0
- package/dist/tool-declaration-http-status.js +12 -0
- package/dist/trace/core-keyset-guard.d.ts +7 -7
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +5 -0
- package/dist/trace/project.d.ts +3 -2
- package/dist/trace/project.js +51 -3
- package/dist/trace/wire-projection-faces.d.ts +1 -1
- package/dist/trace/wire-projection-faces.js +1 -1
- package/dist/unattended-permission-mode.d.ts +110 -0
- package/dist/unattended-permission-mode.js +65 -0
- package/package.json +3 -3
- package/dist/boot/session-shell-gate-registry.d.ts +0 -39
- package/dist/boot/session-shell-gate-registry.js +0 -21
- package/dist/roster-bound-http-status.d.ts +0 -6
- package/dist/roster-bound-http-status.js +0 -6
package/dist/http/resume-legs.js
CHANGED
|
@@ -37,6 +37,7 @@ import { createCapabilitiesLocal } from "./routes/capabilities.js";
|
|
|
37
37
|
import { gatedPrincipal, explicitOperatorOk } from "./principal-gate.js";
|
|
38
38
|
import { buildActiveRunConflict, resumeEntryForGate } from "./active-run-conflict.js";
|
|
39
39
|
import { commitPlacement, devicePlacementErrorBody } from "./device-placement.js";
|
|
40
|
+
import { commitSessionInheritEnv } from "./session-inherit-env.js";
|
|
40
41
|
import { ASK_USER_QUESTION_TOOL_NAME } from "../approval-content-kind.js";
|
|
41
42
|
import { readResumeCtx, resumeCtxCorruptAnswer } from "./resume-ctx-read.js";
|
|
42
43
|
import { httpErrorCode } from "./send.js";
|
|
@@ -265,6 +266,7 @@ export function createResumeLegs(ctx) {
|
|
|
265
266
|
sessionFaceOf: deps.sessionStorage?.sessionFaceOf ? deps.sessionStorage.sessionFaceOf.bind(deps.sessionStorage) : sessionFaceUnavailable,
|
|
266
267
|
...(deps.parkedKnownAgentTypes ? { knownAgentTypes: deps.parkedKnownAgentTypes } : {}),
|
|
267
268
|
...(deps.parkedReviveInheritedGate ? { rebuildInheritedGate: deps.parkedReviveInheritedGate } : {}),
|
|
269
|
+
...(deps.toolApproval ? { approverSeatFor: deps.toolApproval.boundUnattendedAsk } : {}),
|
|
268
270
|
...(deps.logger ? { warn: parkWarn } : {}),
|
|
269
271
|
}, parkedReq, { handle: origin.handle, row: origin.row }));
|
|
270
272
|
void ctxPromise.catch(() => { });
|
|
@@ -297,7 +299,7 @@ export function createResumeLegs(ctx) {
|
|
|
297
299
|
if (!ctx)
|
|
298
300
|
return { status: 409, body: { error: "resume context missing — cannot rebuild task config", errorCode: "conflict.resume_context_unavailable" } };
|
|
299
301
|
const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
|
|
300
|
-
const resolved = await resolveResumeSpecOrBlocked(ctx
|
|
302
|
+
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
301
303
|
if (!resolved.ok)
|
|
302
304
|
return resolved.answer;
|
|
303
305
|
const { spec, engineNotices } = resolved;
|
|
@@ -329,14 +331,18 @@ export function createResumeLegs(ctx) {
|
|
|
329
331
|
}
|
|
330
332
|
const ONE_SHOT_HISTORY_KEYS = ["resumeAt", "restoreFiles", "acceptPartialRestore", "rewindFilesTo", "rewindFiles"];
|
|
331
333
|
const ONE_SHOT_HISTORY_STRIP = Object.fromEntries(ONE_SHOT_HISTORY_KEYS.map((k) => [k, undefined]));
|
|
332
|
-
const resolveResumeSpec = async (
|
|
334
|
+
const resolveResumeSpec = async (ctx, req, auth) => {
|
|
333
335
|
const engineNotices = [];
|
|
334
|
-
const spec = await deps.resolveSpec({ ...body, ...ONE_SHOT_HISTORY_STRIP }, req, auth, {
|
|
336
|
+
const spec = await deps.resolveSpec({ ...ctx.body, ...ONE_SHOT_HISTORY_STRIP }, req, auth, {
|
|
337
|
+
leg: "resume",
|
|
338
|
+
onNotice: (n) => engineNotices.push(n),
|
|
339
|
+
...(ctx.unattendedLeg === true ? { unattendedLeg: true } : {}),
|
|
340
|
+
});
|
|
335
341
|
return { spec, engineNotices };
|
|
336
342
|
};
|
|
337
|
-
const resolveResumeSpecOrBlocked = async (
|
|
343
|
+
const resolveResumeSpecOrBlocked = async (ctx, req, auth) => {
|
|
338
344
|
try {
|
|
339
|
-
const { spec, engineNotices } = await resolveResumeSpec(
|
|
345
|
+
const { spec, engineNotices } = await resolveResumeSpec(ctx, req, auth);
|
|
340
346
|
return { ok: true, spec, engineNotices };
|
|
341
347
|
}
|
|
342
348
|
catch (e) {
|
|
@@ -427,7 +433,7 @@ export function createResumeLegs(ctx) {
|
|
|
427
433
|
if (deps.toolApproval && taskId) {
|
|
428
434
|
const toolApproval = deps.toolApproval;
|
|
429
435
|
resumeTaskConfig.onAsk = approvalLeg.windowZero
|
|
430
|
-
?
|
|
436
|
+
? toolApproval.boundUnattendedAsk({ owner: principal ?? null, taskId, sessionId })
|
|
431
437
|
: toolApproval.boundAsk({
|
|
432
438
|
owner: principal ?? null,
|
|
433
439
|
taskId,
|
|
@@ -491,6 +497,7 @@ export function createResumeLegs(ctx) {
|
|
|
491
497
|
deps.modelUsage?.register(taskId);
|
|
492
498
|
resumeManifestLease = deps.promptManifests?.register(taskId) ?? 0;
|
|
493
499
|
}
|
|
500
|
+
commitSessionInheritEnv(deps, sessionId, resumeCtx.ctx.body);
|
|
494
501
|
if (args.onResumeCommitted)
|
|
495
502
|
await args.onResumeCommitted();
|
|
496
503
|
let unregisterEngineNotice;
|
|
@@ -1061,7 +1068,7 @@ export function createResumeLegs(ctx) {
|
|
|
1061
1068
|
return { status: 409, body: { error: "resume context missing — cannot rebuild task config", errorCode: "conflict.resume_context_unavailable" } };
|
|
1062
1069
|
const ctx = ctxRead.ctx;
|
|
1063
1070
|
const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
|
|
1064
|
-
const resolved = await resolveResumeSpecOrBlocked(ctx
|
|
1071
|
+
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
1065
1072
|
if (!resolved.ok)
|
|
1066
1073
|
return resolved.answer;
|
|
1067
1074
|
const { spec, engineNotices } = resolved;
|
|
@@ -1093,7 +1100,7 @@ export function createResumeLegs(ctx) {
|
|
|
1093
1100
|
if (windowDenial)
|
|
1094
1101
|
return { status: windowDenial.status, body: windowDenial.body };
|
|
1095
1102
|
const auth = { sessionId, principal, memoryScope: ctx.memoryScope };
|
|
1096
|
-
const resolved = await resolveResumeSpecOrBlocked(ctx
|
|
1103
|
+
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
1097
1104
|
if (!resolved.ok)
|
|
1098
1105
|
return resolved.answer;
|
|
1099
1106
|
const { spec, engineNotices } = resolved;
|
|
@@ -1167,7 +1174,7 @@ export function createResumeLegs(ctx) {
|
|
|
1167
1174
|
return { status: 409, body: { error: "resume context missing — cannot rebuild task config", errorCode: "conflict.resume_context_unavailable" } };
|
|
1168
1175
|
const ctx = ctxRead.ctx;
|
|
1169
1176
|
const auth = { sessionId: cp.sessionId, principal: decodeCheckpointScope(cp.scope), memoryScope: ctx.memoryScope };
|
|
1170
|
-
const resolved = await resolveResumeSpecOrBlocked(ctx
|
|
1177
|
+
const resolved = await resolveResumeSpecOrBlocked(ctx, req, auth);
|
|
1171
1178
|
if (!resolved.ok)
|
|
1172
1179
|
return resolved.answer;
|
|
1173
1180
|
const { spec, engineNotices } = resolved;
|
|
@@ -1267,7 +1274,8 @@ export function createResumeLegs(ctx) {
|
|
|
1267
1274
|
}
|
|
1268
1275
|
const approvedMode = decision === "approve" && effMode === "plan" ? (permissionModeAfter ?? "default") : undefined;
|
|
1269
1276
|
const resumeBody = approvedMode === undefined ? ctx.body : { ...ctx.body, permissionMode: approvedMode };
|
|
1270
|
-
const
|
|
1277
|
+
const rebuildCtx = { ...ctx, body: resumeBody };
|
|
1278
|
+
const resolved = await resolveResumeSpecOrBlocked(rebuildCtx, req, auth);
|
|
1271
1279
|
if (!resolved.ok)
|
|
1272
1280
|
return resolved.answer;
|
|
1273
1281
|
const { spec, engineNotices } = resolved;
|
|
@@ -1279,7 +1287,7 @@ export function createResumeLegs(ctx) {
|
|
|
1279
1287
|
...(editedPlan !== undefined ? { editedPlan } : {}),
|
|
1280
1288
|
...(reason ? { reason } : {}),
|
|
1281
1289
|
};
|
|
1282
|
-
const out = await driveResumeIntoRunLog({ runner: runnerFor(spec), token, sessionId: cp.sessionId, principal: auth.principal, fleetScope: resumeFleetScope(req, auth), taskConfig, resumeObjective, outcome, verifyRounds, resumeCtx: { sessionId, ctx:
|
|
1290
|
+
const out = await driveResumeIntoRunLog({ runner: runnerFor(spec), token, sessionId: cp.sessionId, principal: auth.principal, fleetScope: resumeFleetScope(req, auth), taskConfig, resumeObjective, outcome, verifyRounds, resumeCtx: { sessionId, ctx: rebuildCtx }, ...(engineNotices.length > 0 ? { engineNotices } : {}), ...(acceptEarly === true ? { acceptEarly: true } : {}), ...(req !== undefined ? { callerInitiated: true } : {}) });
|
|
1283
1291
|
return out;
|
|
1284
1292
|
}
|
|
1285
1293
|
const routeCtxBase = {
|
package/dist/http/route-ctx.d.ts
CHANGED
|
@@ -147,6 +147,15 @@ export interface CheckpointCtx {
|
|
|
147
147
|
body: TaskRequestBody;
|
|
148
148
|
/** 提交时这条会话绑定的记忆域(重建 `RequestAuth` 用);缺席 = 没有记忆面。 */
|
|
149
149
|
memoryScope?: string;
|
|
150
|
+
/**
|
|
151
|
+
* 🔴 S-877 候裁 18(codex r3 F1)—— 这条会话的 run 是**无人值守腿**铸的(今天唯一的写者 = leader worker 的 durable 块,
|
|
152
|
+
* `leader/wire.ts`),**服务端写、不来自请求体**(`body` 是提交体;本位是它的兄弟键,客户端够不着)。
|
|
153
|
+
* 续跑家族据此请 `resolveSpec` 带 `unattendedLeg: true`,由它按续跑 Runner 的同名座经 `createUnattendedLeg` / `withUnattendedLeg`
|
|
154
|
+
* 重挂整条腿(模式 = 旋钮、写面 roots、模式释放写保护时的写保护拒层)—— 拒层是活闭包、进不了 checkpoint,而 core 在 spec 不说
|
|
155
|
+
* 模式时取 checkpoint 记下的那一个(`seat ?? recorded`),不重挂 = 续跑落 bypass 而拒层缺席。写回口(`DriveResumeArgs.resumeCtx`)
|
|
156
|
+
* 传原 ctx / 其展开 ⇒ 标随行。缺席 = 不是无人值守腿(HTTP 提交腿;7.106.0 前落盘的行 —— 那时这些腿不铸模式,checkpoint 里也没有)。
|
|
157
|
+
*/
|
|
158
|
+
unattendedLeg?: true;
|
|
150
159
|
}
|
|
151
160
|
/** B4①(命名化):`driveResumeIntoRunLog` 的入参形(resume 家族共用)——原为 9 成员内联匿名对象。 */
|
|
152
161
|
export interface DriveResumeArgs {
|
|
@@ -347,7 +347,6 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
347
347
|
...(s.mandate !== undefined ? { mandate: s.mandate } : {}),
|
|
348
348
|
...(s.denialLimitFallback !== undefined ? { denialLimitFallback: s.denialLimitFallback } : {}),
|
|
349
349
|
...(s.origin !== undefined ? { origin: s.origin } : {}),
|
|
350
|
-
...(s.ruleStoreUnreadable !== undefined ? { ruleStoreUnreadable: s.ruleStoreUnreadable } : {}),
|
|
351
350
|
objective,
|
|
352
351
|
input,
|
|
353
352
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { expandTiers, reasoningEffortLevelsOf, routePairingStatus } from "@sema-agent/core";
|
|
2
2
|
import { isModelAllowlisted, selectableCatalog } from "../../model-select.js";
|
|
3
|
-
import { resolveModelApiKey } from "../../key-resolver.js";
|
|
4
|
-
import {
|
|
3
|
+
import { modelKeyPoisonOf, resolveModelApiKey } from "../../key-resolver.js";
|
|
4
|
+
import { projectSealedKeyCapability } from "../../sealed-key.js";
|
|
5
5
|
import { cwdHonored, deviceCwdHonored } from "../../task-cwd.js";
|
|
6
|
-
import {
|
|
6
|
+
import { taskWriteFaceRefusal } from "../../hosted-posture.js";
|
|
7
7
|
import { autonomyOverrides } from "../../runtime-governance.js";
|
|
8
8
|
import { DEVICE_PROTOCOL_VERSION, DEVICE_WS_PATH } from "../../device-ws-protocol.js";
|
|
9
9
|
import { a2aInjectionHonored } from "../../task-a2a.js";
|
|
@@ -16,9 +16,10 @@ import { fileHistoryCaptureOf } from "../../file-history-capture.js";
|
|
|
16
16
|
import { resolveStreamApprovalGate } from "../../tool-approval.js";
|
|
17
17
|
import { projectMemoryEngineCapability } from "../../memory-posture.js";
|
|
18
18
|
import { projectWriteProtectionCapability } from "../../write-protection.js";
|
|
19
|
-
import {
|
|
19
|
+
import { projectStoreCapability } from "../../store-posture.js";
|
|
20
20
|
import { projectWebSearchCapability } from "../../plugins/web-search.js";
|
|
21
21
|
import { projectExecutionLaneCapability } from "../../capabilities/execution-lane.js";
|
|
22
|
+
import { gateCompositeMembers } from "../../capabilities/composite-members.js";
|
|
22
23
|
import { projectApprovalsStreamLiveCapability } from "../../capabilities/approvals-push.js";
|
|
23
24
|
import { workflowModelAllowlistFor, selfOrchestrationDenial } from "../../task-workflow.js";
|
|
24
25
|
import { centerEntitlementSourceWired } from "../../runtime-caps-resolver.js";
|
|
@@ -27,7 +28,7 @@ import { coercePermissionModeStrict, SETTINGS_PERMISSION_MODE_WORDS } from "../.
|
|
|
27
28
|
import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
|
|
28
29
|
import { sessionBackgroundFacesAvailable } from "./background.js";
|
|
29
30
|
function routePairingOf(deps, name, m) {
|
|
30
|
-
if (
|
|
31
|
+
if (modelKeyPoisonOf(name, deps.config.modelApiKeyEnv ?? {}, process.env, deps.config.modelApiKeys ?? {}) !== undefined)
|
|
31
32
|
return "unknown";
|
|
32
33
|
const key = resolveModelApiKey(name, deps.config.modelApiKeyEnv ?? {}, process.env, deps.config.modelApiKeys ?? {});
|
|
33
34
|
return routePairingStatus(deps.routeJudgeBrain ?? {}, m, key !== undefined ? { apiKey: key } : undefined);
|
|
@@ -65,11 +66,11 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
65
66
|
return;
|
|
66
67
|
}
|
|
67
68
|
const entitlementSource = centerEntitlementSourceWired(deps.config);
|
|
68
|
-
const { fault: autoModeFault, ...autoMode } = await judgeAutoModeArming(deps.autoModeFace, { requestedMode, principal: gatedPrincipal(req, deps.config),
|
|
69
|
+
const { fault: autoModeFault, ...autoMode } = await judgeAutoModeArming(deps.autoModeFace, { requestedMode, principal: gatedPrincipal(req, deps.config), localDeny: deps.config.permissionsDisableAutoMode });
|
|
69
70
|
if (autoModeFault !== undefined)
|
|
70
71
|
deps.logger?.warn("auto_mode_face_resolver_fault", { fault: autoModeFault });
|
|
71
72
|
const rulesLaneReachable = Boolean(deps.ruleConsent) && gatedPrincipal(req, deps.config) !== undefined;
|
|
72
|
-
sendJson(res, 200, {
|
|
73
|
+
sendJson(res, 200, gateCompositeMembers({
|
|
73
74
|
...deps.capabilities,
|
|
74
75
|
asyncRuns: Boolean(deps.runStore),
|
|
75
76
|
artifacts: Boolean(deps.runStore),
|
|
@@ -88,7 +89,8 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
88
89
|
memory: false,
|
|
89
90
|
memoryWrite: false,
|
|
90
91
|
memoryEngine: projectMemoryEngineCapability(deps.memoryPosture),
|
|
91
|
-
|
|
92
|
+
store: projectStoreCapability(deps.storePosture ?? { declared: deps.config.dbBackend, backend: "memory" }, deps.sqlEngineFacts?.()),
|
|
93
|
+
centerWiring: deps.centerWiring?.(gatedPrincipal(req, deps.config)),
|
|
92
94
|
webSearch: projectWebSearchCapability(deps.webSearchProvider),
|
|
93
95
|
sessionBackgroundable: Boolean(deps.runStore),
|
|
94
96
|
runMemoryCaptureOptOut: Boolean(deps.runStore),
|
|
@@ -125,7 +127,7 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
125
127
|
accepted: true,
|
|
126
128
|
classifierSeat: deps.autoModeFace?.seatMounted === true,
|
|
127
129
|
entitlementSource,
|
|
128
|
-
intentArming:
|
|
130
|
+
intentArming: true,
|
|
129
131
|
...autoMode,
|
|
130
132
|
},
|
|
131
133
|
readFace: deps.config.readFace ?? null,
|
|
@@ -141,7 +143,6 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
141
143
|
projectContext: cwdHonored(deps.config),
|
|
142
144
|
callerCwd: cwdHonored(deps.config) || deviceCwdHonored(deps.config),
|
|
143
145
|
taskWriteFaceOpen: taskWriteFaceRefusal(deps.config) === undefined && autonomyOverrides(deps.config.autonomy).writeFace !== "roots",
|
|
144
|
-
taskApproverPosture: taskApproverPostureHonored(deps.config),
|
|
145
146
|
executionLane: projectExecutionLaneCapability(deps.config),
|
|
146
147
|
mcpInjection: mcpInjectionHonored(deps.config),
|
|
147
148
|
a2a: true,
|
|
@@ -200,7 +201,7 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
200
201
|
manualCompact: Boolean(deps.runStore),
|
|
201
202
|
configCatalog: deps.config.configCatalogEnabled === true && deps.config.operatorPrincipals.length > 0,
|
|
202
203
|
sealedKey: projectSealedKeyCapability(deps.sealedKeyPublic),
|
|
203
|
-
});
|
|
204
|
+
}));
|
|
204
205
|
return;
|
|
205
206
|
}
|
|
206
207
|
case "capability-mcp": {
|
|
@@ -2,7 +2,7 @@ import { DOMAINS } from "@sema-agent/settings-schema";
|
|
|
2
2
|
import { sendJson, sendError } from "../send.js";
|
|
3
3
|
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
4
4
|
import { assertNever } from "../route-ctx.js";
|
|
5
|
-
import { configKnobTable } from "../../config.js";
|
|
5
|
+
import { buildRetiredEnvWire, configKnobTable } from "../../config.js";
|
|
6
6
|
import { buildConfigCatalog } from "../../config-catalog.js";
|
|
7
7
|
export const CONFIG_CATALOG_PATH = "/v1/config/catalog";
|
|
8
8
|
const OPERATOR_ONLY_MESSAGE = "the config catalog is operator-only: the caller principal is not in OPERATOR_PRINCIPALS (an empty list means NO one is an operator — configure it to enable this endpoint)";
|
|
@@ -27,6 +27,7 @@ export async function handleConfigCatalog(req, res, match, ctx) {
|
|
|
27
27
|
knobs: configKnobTable(),
|
|
28
28
|
centerDomains: DOMAINS,
|
|
29
29
|
centerManagedKeys: deps.configCenterManagedKeys?.(),
|
|
30
|
+
retired: buildRetiredEnvWire(),
|
|
30
31
|
}));
|
|
31
32
|
return;
|
|
32
33
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { buildPostureKnobReading } from "../../posture-source.js";
|
|
2
2
|
import { buildReadFaceWiring } from "../../read-face-posture.js";
|
|
3
|
+
import { projectExecutionLaneCapability } from "../../capabilities/execution-lane.js";
|
|
3
4
|
import { resolveStreamApprovalGate } from "../../tool-approval.js";
|
|
4
5
|
import { sendJson, sendError } from "../send.js";
|
|
5
6
|
import { gatedPrincipal, explicitOperatorOk, isLoopbackAddress, verifiedIdentitySource } from "../principal-gate.js";
|
|
@@ -104,6 +105,7 @@ export async function handleDiagnostics(req, res, match, ctx) {
|
|
|
104
105
|
sqlEngine: deps.sqlEngineFacts?.() ?? null,
|
|
105
106
|
writeProtection: deps.writeProtection ?? null,
|
|
106
107
|
readFace: buildReadFaceWiring(deps.config),
|
|
108
|
+
executionLane: projectExecutionLaneCapability(deps.config),
|
|
107
109
|
configApply: deps.configApplyState?.() ?? null,
|
|
108
110
|
mcpRevocations: deps.mcpRevocationState?.() ?? null,
|
|
109
111
|
serverGates: buildServerWiringGates({
|
|
@@ -22,6 +22,7 @@ import { buildApprovalPreamble, buildApprovalPreambleSseFrames } from "../../app
|
|
|
22
22
|
import { createApprovalCardEmitter, resolveApprovalLeg, resolveStreamApprovalGate } from "../../tool-approval.js";
|
|
23
23
|
import { buildActiveRunConflict, toDoneFrameResult } from "../active-run-conflict.js";
|
|
24
24
|
import { commitPlacement, devicePlacementErrorBody } from "../device-placement.js";
|
|
25
|
+
import { commitSessionInheritEnv } from "../session-inherit-env.js";
|
|
25
26
|
import { headerStr, gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
26
27
|
import { failOpenTagForDroppedFrame, recordFailOpen } from "../../observability/fail-open.js";
|
|
27
28
|
import { buildSyncSubmitBody } from "../sync-submit-body.js";
|
|
@@ -248,6 +249,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
248
249
|
}
|
|
249
250
|
durableTaskId = tid;
|
|
250
251
|
}
|
|
252
|
+
commitSessionInheritEnv(deps, prepared.spec.sessionId, prepared.body);
|
|
251
253
|
if (prepared.spec.sessionId)
|
|
252
254
|
markChildrenStoppedByUserOnAbort(ac.signal, durableTaskId ?? prepared.spec.sessionId, prepared.auth?.principal);
|
|
253
255
|
let ledgerSink;
|
|
@@ -633,7 +635,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
633
635
|
if (deps.toolApproval) {
|
|
634
636
|
const toolApproval = deps.toolApproval;
|
|
635
637
|
prepared.spec.onAsk = windowZero
|
|
636
|
-
?
|
|
638
|
+
? toolApproval.boundUnattendedAsk({ owner: askOwner, taskId: askTaskId, ...(prepared.spec.sessionId ? { sessionId: prepared.spec.sessionId } : {}) })
|
|
637
639
|
: toolApproval.boundAsk({
|
|
638
640
|
owner: askOwner,
|
|
639
641
|
taskId: askTaskId,
|
|
@@ -795,6 +797,7 @@ export async function handleTasks(req, res, match, ctx) {
|
|
|
795
797
|
deps.sessionTitler?.maybeTitle(prepared.spec.sessionId, prepared.spec.objective, typeof prepared.spec.model === "string" ? prepared.spec.model : prepared.spec.model?.id);
|
|
796
798
|
durableTaskId = tid;
|
|
797
799
|
}
|
|
800
|
+
commitSessionInheritEnv(deps, prepared.spec.sessionId, prepared.body);
|
|
798
801
|
if (deps.checkpointStore && prepared.spec.sessionId) {
|
|
799
802
|
await deps.checkpointStore.putCtx(prepared.spec.sessionId, { body: prepared.body, memoryScope: prepared.auth?.memoryScope });
|
|
800
803
|
}
|
package/dist/http/server.d.ts
CHANGED
|
@@ -104,11 +104,15 @@ export interface ServiceCoreDeps {
|
|
|
104
104
|
* 缺席(桩)⇒ 只走结构化日志终点,行为逐字不变。
|
|
105
105
|
* ⚠️ 7.69.0(R-1)起 **resume 家族也递这一位** —— 四条 resume 腿收敛到 `resolveResumeSpec` 一只 resolver,
|
|
106
106
|
* 通告口接一次、四腿同得(见 http/server.ts 的 `resolveResumeSpec` 顶注);此处旧注写作「resume 家族缺席」
|
|
107
|
-
* 已过期,按新行为改。
|
|
107
|
+
* 已过期,按新行为改。
|
|
108
|
+
* `opts.unattendedLeg`(S-877 候裁 18,codex r3 F1):续跑家族在 ctx 带无人值守标(`CheckpointCtx.unattendedLeg`)时递 `true` ——
|
|
109
|
+
* 装配器在**全部折叠之后**经 `createUnattendedLeg` / `withUnattendedLeg` 重挂那条腿(表 / 根取续跑 Runner 的同名座)。只由续跑腿
|
|
110
|
+
* 按服务端写的 ctx 位递,绝不从请求体读。 */
|
|
108
111
|
resolveSpec: (body: TaskRequestBody, req: IncomingMessage | undefined, auth?: RequestAuth, opts?: {
|
|
109
112
|
leg?: "fresh" | "resume";
|
|
110
113
|
liveStream?: boolean;
|
|
111
114
|
onNotice?: (notice: EngineNotice) => void;
|
|
115
|
+
unattendedLeg?: true;
|
|
112
116
|
}) => TaskSpec | Promise<TaskSpec>;
|
|
113
117
|
/**
|
|
114
118
|
* [ref] 场景 hands lane 选路:按 `resolveSpec` 刚产出的 TaskSpec 取本任务的执行 Runner。
|
|
@@ -339,6 +343,12 @@ export interface ServiceSeamDeps {
|
|
|
339
343
|
* 所以三者的在场性是同一个事实;族内 501 谓词因此可以是「三件齐」这一句话。
|
|
340
344
|
*/
|
|
341
345
|
deviceEnrollment?: import("../device-enrollment.js").DeviceEnrollment;
|
|
346
|
+
/**
|
|
347
|
+
* S-895 × 7.106.0 合并复审 X1:host 车道会话登记簿的 `inheritEnv` **写口**(`boot/execution-env.ts` 的 `setSessionInheritEnv`)。
|
|
348
|
+
* 唯一调用者 = `http/session-inherit-env.ts` 的 `commitSessionInheritEnv`(四个 run 认领点在认领之后调;判据与残余见那只文件)。
|
|
349
|
+
* 缺席 = 本部署不是 host 车道(没有工厂读这一格)⇒ 提交点 no-op。装配 = `boot/stage-10-http-server.ts`(host 车道才给)。
|
|
350
|
+
*/
|
|
351
|
+
bindSessionInheritEnv?: import("./session-inherit-env.js").SessionInheritEnvCommitCtx["bindSessionInheritEnv"];
|
|
342
352
|
/** Audit回溯: current context (+ summary) for a session, plus its owner. Enables GET /v1/sessions/:id. */
|
|
343
353
|
sessionAudit?: (sessionId: string) => Promise<({
|
|
344
354
|
owner: string | null;
|
|
@@ -651,8 +661,14 @@ export interface ServiceDeploymentDeps {
|
|
|
651
661
|
* main.ts passes the effective store label (`durable(pg)` / `durable(mysql)` / `durable(local)` / "memory",
|
|
652
662
|
* same derivation as the boot `listening` log). Absent → fall back to the raw config enum. */
|
|
653
663
|
sessionStoreLabel?: string;
|
|
654
|
-
/** S5
|
|
655
|
-
|
|
664
|
+
/** S5 → S-866 ①:持久化姿态的**唯一**装配值(`store-posture.ts`,`boot/stores.ts` 铸):`/health` 的 `storeDegraded` / `durable:false`
|
|
665
|
+
* (present-iff `degradedReason` 在场)与 caps `store` 同读它,gauge `store_backend_degraded` 在装配点读同一只。
|
|
666
|
+
* 缺席 = 这个进程不是由 composition root 装起来的(测试夹具形)⇒ `/health` 不带降级位、caps `store` 取诚实下限
|
|
667
|
+
* (`declared` = 配置词,`backend:"memory"` —— 本面不知道任何装出来的后端)。 */
|
|
668
|
+
storePosture?: import("../store-posture.js").StorePosture;
|
|
669
|
+
/** S-866 ②:caps `centerWiring` 的读面(`capabilities/center-wiring.ts` `createCenterWiringReader`;入参 = 请求的已验 principal)。
|
|
670
|
+
* 缺席 = 没接中心(或测试夹具)⇒ 键整个缺席。 */
|
|
671
|
+
centerWiring?: (principal: string | undefined) => import("../capabilities/center-wiring.js").CenterWiring;
|
|
656
672
|
/**
|
|
657
673
|
* Graceful drain — shared mutable state between main.ts (flips `draining` on SIGTERM) and this server:
|
|
658
674
|
* • while `draining`, NEW model-running submissions (the billable submit paths) get 503 + Retry-After, so an
|
|
@@ -754,7 +770,7 @@ export interface ServiceDeploymentDeps {
|
|
|
754
770
|
/** S-131:SQL 引擎姿态的 **live getter**(`sql-engine-posture.ts` 的单一推导点 —— boot 期借一条池连接
|
|
755
771
|
* 触发驱动的连接初始化,读的是 `SET` 之后**回读复核**过的真值)。两个读面共用:
|
|
756
772
|
* `GET /v1/diagnostics/wiring` 的 `sqlEngine` 段(operator-only,全量含版本)与 `GET /v1/capabilities`
|
|
757
|
-
* 的 `sql` 位(
|
|
773
|
+
* 的 `store.sql` 位(窄投影;7.106.0 前是顶层 `sql`,S-866 ①)。getter 而不是快照:池重连后驱动会重新初始化并刷新事实,快照会说谎。
|
|
758
774
|
* 缺席 / 返回 null = 本部署没有 SQL 后端(env-only worker、local 文件后端)—— 诚实缺席,不铸空壳。 */
|
|
759
775
|
sqlEngineFacts?: () => import("../plugins/sql-driver.js").SqlEngineFacts | null;
|
|
760
776
|
/** S-138:**写保护名表**的姿态(`write-protection.ts` 的 boot 期单一推导点 —— 与递给每只 Runner 的
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S-895 × 7.106.0 合并复审 X1(gpt-6 腿 A F1 [high],验证腿 B verified)—— 请求键 `inheritEnv` 的**会话绑定唯一提交点**。
|
|
3
|
+
*
|
|
4
|
+
* ── 分界(与 `device-placement.ts` 的首绑写协议同一句判据)──────────────────────────────────────────────────
|
|
5
|
+
* 凡需要「这条会话此刻归我这条 run」才成立的事,排在 run 认领之后;其余全排在建任何东西之前。`inheritEnv` 的三道判
|
|
6
|
+
* (值形 / 车道 / 射程)是**读**,住在 `resolveSpec`(两腿同判,任何副作用之前);而「本会话的 host 工厂下一次造环境按哪个词」
|
|
7
|
+
* 是**写**,它只属于一条**被受理**的 run —— 修前它写在 `resolveSpec` 的最后一步,仍先于路由层的拒(`/v1/tasks/stream` 的
|
|
8
|
+
* verify / cascade 400 `request.field_conflict`、会话占用 409、`/v1/runs` 的建行 409、placement 拒、续跑的 CAS 输 / 配额 429 …),
|
|
9
|
+
* 一条被拒的 `"all"` 于是把同会话上一条已受理的 `"scrub"` 放宽成整份继承。
|
|
10
|
+
*
|
|
11
|
+
* ── 调用点(类级机器门钉住:`test/inherit-env-request-key.test.ts`「X1 类级机器门」)───────────────────────────
|
|
12
|
+
* 四个 run 认领点在认领(与 placement 提交)成功之后、起跑之前各调一次 —— 流式提交 / 同步提交(`routes/tasks.ts`)、durable 建行腿
|
|
13
|
+
* (`durable-run-leg.ts`,`/v1/runs` · A2A · `/decide` 第三车道共用)、续跑驱动腿(`resume-legs.ts` 的 `driveResumeLeg`,markResuming
|
|
14
|
+
* 赢或 wake 新行建成之后)。无 run 店的部署没有认领这回事:调用点落在认领段**之后**的同一位置,等价于「起跑前、一切拒之后」。
|
|
15
|
+
* 写的值 = 受理时那条请求体的键(续跑腿 = 盘上那条体的键);缺席 = 清掉(缺省清洗;理由在 `setSessionInheritEnv` 顶注)。
|
|
16
|
+
*
|
|
17
|
+
* ── 残余(如实,S-908)──────────────────────────────────────────────────────────────────────────────────────
|
|
18
|
+
* 登记簿仍按**会话**键控、host 工厂在 run 起跑那一刻现读(core `ExecutionEnvFactoryContext` 只带 sessionId / taskId,没有 per-run 座)。
|
|
19
|
+
* 有 run 店的部署上认领本身串行化同会话的 run(`task_active` 唯一键),「写 → 读」落在同一条认领窗里;没有 run 店的部署上同会话
|
|
20
|
+
* 并发受理的两条 run 仍是「最后写者赢」。续跑腿在 markResuming 赢之后写,core 的 pre-CAS 守卫(D-1 绑定 / 过期 token)之后才拒的续跑
|
|
21
|
+
* 已写下**那条 park 任务自己**盘上的值(不是别的请求的),下一条受理的 run 照写覆盖。根治 = core 的 per-run 座(S-908)。
|
|
22
|
+
* 射程只在单用户 host 部署(`"all"` 的射程门)⇒ 写者恒是同一主人。
|
|
23
|
+
*/
|
|
24
|
+
import type { TaskInheritEnv } from "../request-value-closure.js";
|
|
25
|
+
/** 提交点的依赖 —— 逐字取自 `ServiceDeps` 的同名键(同一只 deps 对象喂进来)。 */
|
|
26
|
+
export interface SessionInheritEnvCommitCtx {
|
|
27
|
+
/** host 车道会话登记簿的写口(`boot/execution-env.ts` 的 `setSessionInheritEnv`);缺席 = 本部署不是 host 车道 ⇒ 没有消费点。 */
|
|
28
|
+
bindSessionInheritEnv?: ((sessionId: string, policy: TaskInheritEnv | undefined) => void) | undefined;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 在 run 认领之后调用:把受理的那条请求的 `inheritEnv`(缺席 = 清)写进本会话的绑定。`sessionId` 缺席(不接 authorizer 的嵌入形)⇒
|
|
32
|
+
* 不写 —— `"all"` 在那一形已被 `resolveSpec` 响亮拒,`"scrub"` / 缺席与工厂缺省同形。
|
|
33
|
+
*/
|
|
34
|
+
export declare function commitSessionInheritEnv(ctx: SessionInheritEnvCommitCtx, sessionId: string | undefined, body: {
|
|
35
|
+
readonly inheritEnv?: TaskInheritEnv | undefined;
|
|
36
|
+
}): void;
|
|
37
|
+
//# sourceMappingURL=session-inherit-env.d.ts.map
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* cut: ONLY TaskRequestBody (its field types are inline or core imports — no other server.ts-local satellites);
|
|
7
7
|
* sibling request/response shapes can migrate here later on the same pattern.
|
|
8
8
|
*/
|
|
9
|
-
import type { A2aServerSpec, AgentDefinition,
|
|
9
|
+
import type { A2aServerSpec, AgentDefinition, McpServerSpec, SkillSpec } from "@sema-agent/core";
|
|
10
10
|
import type { SettingsPermissionMode } from "../task-settings.js";
|
|
11
11
|
import type { wiringManifestFaceEventData } from "../trace/project.js";
|
|
12
12
|
import type { buildSyncSubmitBody } from "./sync-submit-body.js";
|
|
@@ -197,8 +197,9 @@ export interface TaskRequestBody {
|
|
|
197
197
|
* 这条 run 零历史店调用:编辑照常落盘,但不做首次触碰备份、不铸回合边界、不做 fork 携带;委派出去的子代继承
|
|
198
198
|
* (core `ParentSpecSeat` 从本位铸,本仓零接线)。`wiring_manifest.fileHistory` 报 `"disabled"`。
|
|
199
199
|
* 同一个提交里再要还原(`restoreFiles` / `rewindFilesTo` / 退役 `rewindFiles`)= 体内矛盾 ⇒ **受理时** 400
|
|
200
|
-
* `request.field_conflict`(`request-value-closure.ts` 的 `FILE_HISTORY_DISABLED_NO_RESTORE`)
|
|
201
|
-
* 落在会话按 `resumeAt`
|
|
200
|
+
* `request.field_conflict`(`request-value-closure.ts` 的 `FILE_HISTORY_DISABLED_NO_RESTORE`):立门时 core 的 `rewind.capture_disabled`
|
|
201
|
+
* 落在会话按 `resumeAt` 分叉之后、拒后不回滚,所以本层在副作用之前拒(S-865 / core 7.35.0 #1195 起 core 在分叉**之前**拒,
|
|
202
|
+
* 本层 400 照旧有效)。续跑腿不过这道门。
|
|
202
203
|
* ⚠️ 拼写门**两腿同判、响亮拒**:fresh 400 `request.field_invalid`;resume 重放同判(与 `memoryCapture` / `memory`
|
|
203
204
|
* 同一张单成员闭集表,`request-value-closure.ts` 的 `singleMemberOffFieldIssue`)。
|
|
204
205
|
* Rides the persisted body onto resume legs —— 它是这条 run 的策略,不是一次性历史动作(与 `rewindFilesTo` 不同,续跑不剥)。 */
|
|
@@ -330,31 +331,6 @@ export interface TaskRequestBody {
|
|
|
330
331
|
* `SETTINGS_PERMISSION_MODES`(受理腿 `isSettingsPermissionMode` 与两句 400 文案同读那一份)。
|
|
331
332
|
* 改前这里是第 6 份手抄的五词联合 —— 加/退一个模式时 wire 型面与受理腿会各走各的。 */
|
|
332
333
|
permissionMode?: SettingsPermissionMode;
|
|
333
|
-
/**
|
|
334
|
-
* **#1000**(core 7.26.0 `TaskSpec.approverPosture`;CC-116 装配口)—— 宿主对这个 run 的**审批姿态声明**:
|
|
335
|
-
* 「这些卡由我自己自动答」。一个词(`"bypass"`),闭集属主在 core(`ApproverPosture` / `APPROVER_POSTURES`)。
|
|
336
|
-
*
|
|
337
|
-
* 🔴 **它不授权任何东西,也不是一只门**(core 头注逐字:"a statement about who answers this run's approval
|
|
338
|
-
* cards, never an instruction to the gate";引擎只把它**渲**给模型,没有任何裁决腿读它)。所以它与
|
|
339
|
-
* {@link permissionMode} 的 `"bypassPermissions"` **不是**一回事,也不能互相派生:后者选的是**门的形状**
|
|
340
|
-
* (且只能收紧、deny-wins),前者只说「谁来答卡」。两者在语义上同族,声明它而不开 `bypassPermissions`
|
|
341
|
-
* 只是对模型说了一句与实际不符的话 —— 见契约文档那一段的「谁受伤」。
|
|
342
|
-
*
|
|
343
|
-
* 🔴 **值域上本仓零判据**(`boot/resolve-spec.ts` 写点):词的拒绝逻辑属引擎
|
|
344
|
-
* (`refuseInvalidApproverPosture` 响亮拒一个不认识的词),server **不抄词表、不加第二道值域门** ——
|
|
345
|
-
* 在下游再判一次就是同一语义面的第二个判官(源头修复纪律 [ref]),而且本仓抄一份词表的那天 core 加
|
|
346
|
-
* 第二个词就会被静默 400 掉。型也从 core 的 `ApproverPosture` 派生(不抄字面联合)。
|
|
347
|
-
*
|
|
348
|
-
* 🔴 **但「原样过境」自 7.95.0(S-591 腿③)起不再成立 —— 它有一道**准入**闸**:本键是一句**宿主姿态
|
|
349
|
-
* 声明**(「这个 run 的审批卡由宿主自己自动答」),而在**托管 / 多租**部署上提交方**不是**宿主。
|
|
350
|
-
* 所以托管形上它被**忽略 + 留声**(`task_approver_posture_ignored`,`honored:false`),与同族的
|
|
351
|
-
* `settings.hooks` / `cwd` / `settings.env` 三条逐字同姿势;单用户 turnkey 与 device 车道照旧过境
|
|
352
|
-
* (射程判据单点 = `src/hosted-posture.ts` 的 `taskApproverPostureHonored`)。
|
|
353
|
-
* ⚠️ **刻意不是 400/422**:本键零授权,而续跑腿逐字重放盘上的体、调用方删不掉它 ⇒ 拒会把一条 parked
|
|
354
|
-
* 任务永久砖死(clay 裁定① 2026-09-22;本仓成文结论「砖死 resume 比降级糟」)。
|
|
355
|
-
* ⇒ 这一条与「值域零判据」**不矛盾**:值域仍归引擎,本仓判的是**这台部署认不认这句声明**。
|
|
356
|
-
*/
|
|
357
|
-
approverPosture?: ApproverPosture;
|
|
358
334
|
/** MF-30 memory PAUSE (shell-host contract, option B per-request — clay 2026-06-27): `false` makes THIS run
|
|
359
335
|
* read-only over long-term memory (`TaskSpec.memory.writeScope:null` — the [ref] memory ENGINE materializes/
|
|
360
336
|
* reads but its harvest commits nothing). Absent/`true` ⇒ normal read+write. The shell's `/memory` pause carries
|
|
@@ -418,6 +394,21 @@ export interface TaskRequestBody {
|
|
|
418
394
|
* (a caller can't point a shared/cloud worker at an arbitrary host path). Validated absolute (prepareSpec 400s a
|
|
419
395
|
* relative cwd — it would silently resolve against the SERVICE process cwd). */
|
|
420
396
|
cwd?: string;
|
|
397
|
+
/**
|
|
398
|
+
* S-895(7.106.0,core [ref] [ref] ②)—— 模型驱动的 host shell **继承多少用户环境**,由调用方按请求声明。
|
|
399
|
+
* 闭集两词(词表外 ⇒ 400 `request.field_invalid`,两腿同判,判据属主 `request-value-closure.ts` 的 `inheritEnvFieldIssue`):
|
|
400
|
+
* · `"all"` —— 整份继承(`{ ...process.env }` 逐字原样,CC 交互形);只在**单用户**部署兑现,射程 = 写容纳面那一只
|
|
401
|
+
* `hostPostureDeclarationsHonored`(`hosted-posture.ts`)⇒ 托管部署(`REQUIRE_PRINCIPAL=true`)响亮拒
|
|
402
|
+
* 422 `request.host_posture_refused`;
|
|
403
|
+
* · `"scrub"` —— 缺省清洗(剥凭据形变量),**任何车道、任何部署**都受理(它就是缺省;7.106.0 合并复审 X7)。
|
|
404
|
+
* **缺席 = 缺省清洗**:不带键的请求把本会话的绑定清掉(不继承上一条请求的声明 —— 这一位是「本条请求要什么」,
|
|
405
|
+
* 粘到下一条上就是替一个没说话的请求放宽)。名单形(`string[]`)**不上 wire**(那是嵌入方构造执行环境的选项)。
|
|
406
|
+
* 🔒 **只有 host 车道有消费点**:别的车道(e2b / k8s / local-docker / ssh / adb / device / in-process)带 `"all"` ⇒
|
|
407
|
+
* 400 `request.field_invalid`「applies to the host lane only」,不静默忽略、不映射到别的车道的 env 名单;`"scrub"` 在那些车道上照受理(无消费点,无害)。
|
|
408
|
+
* Rides the persisted body onto resume legs:重放照射程与车道重判(部署由单用户改托管后 revive ⇒ 同一只拒,
|
|
409
|
+
* resume-legs 折成 `409 resume_blocked_by_policy`)。
|
|
410
|
+
*/
|
|
411
|
+
inheritEnv?: "all" | "scrub";
|
|
421
412
|
/**
|
|
422
413
|
* S-470(device lane 首绑写协议,design `device-executor-lane-v2` §4.3.2)—— 这条**根会话**要执行在
|
|
423
414
|
* 哪台已注册的员工设备上(`POST /v1/devices/enroll` 返回的 `dev_<ulid>`)。
|
package/dist/index.d.ts
CHANGED
|
@@ -26,5 +26,6 @@ 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
27
|
export { createSideQueryLane, createPerModelKeyBrain, type KeyResolver, type SideQueryLane, type SideQueryLaneCtx } from "./boot/side-query-lane.js";
|
|
28
28
|
export { createKeyResolver, resolveModelApiKey, type ModelKeyRef } from "./key-resolver.js";
|
|
29
|
-
export {
|
|
29
|
+
export { isModelKeyPoison, ModelKeyPoisonedError, type ModelKeyPoison, type ModelKeyPoisonReason } from "./key-resolver.js";
|
|
30
|
+
export { RETIRED_ENVS, type RetiredEnv, type RetiredEnvKind, type RetiredEnvWire } from "./config.js";
|
|
30
31
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -23,5 +23,6 @@ export { memoryScopeFor, memoryEngineBackendFor } from "./memory-scope.js";
|
|
|
23
23
|
export { createHttpServer, } from "./http/server.js";
|
|
24
24
|
export { createSideQueryLane, createPerModelKeyBrain } from "./boot/side-query-lane.js";
|
|
25
25
|
export { createKeyResolver, resolveModelApiKey } from "./key-resolver.js";
|
|
26
|
-
export {
|
|
26
|
+
export { isModelKeyPoison, ModelKeyPoisonedError } from "./key-resolver.js";
|
|
27
|
+
export { RETIRED_ENVS } from "./config.js";
|
|
27
28
|
//# sourceMappingURL=index.js.map
|
package/dist/key-resolver.d.ts
CHANGED
|
@@ -1,27 +1,79 @@
|
|
|
1
1
|
import type { Model } from "@sema-agent/core";
|
|
2
|
-
|
|
2
|
+
/** 托管密钥解不开的三个成因(**托管**失败;密文形错归 S-704 `config.model_entry_invalid`,旧词 `unsupported_alg` 已随之退役)。 */
|
|
3
|
+
export type SealedCustodyPoisonReason = "sealed_decrypt_failed" | "stale_ciphertext" | "no_key_store";
|
|
4
|
+
/** 毒丸成因全集(闭集;拒句按它穷举)。 */
|
|
5
|
+
export type ModelKeyPoisonReason = SealedCustodyPoisonReason | "env_missing";
|
|
6
|
+
/**
|
|
7
|
+
* 毒丸 —— **只带诊断**(来源身份 + 成因 + 检测时刻),从不带密文、明文或变量的值(每一个序列化面平凡地零秘密)。
|
|
8
|
+
* · `source:"sealed"`:`publicKeyId` = 密文指向的钥匙对;`errKind` = 解封失败的错误**类名**(不是消息;消息在植入那一刻已进日志)。
|
|
9
|
+
* · `source:"env"`:`apiKeyEnv` = 目录声明的变量**名**。
|
|
10
|
+
*/
|
|
11
|
+
export type ModelKeyPoison = {
|
|
12
|
+
readonly modelKeyPoison: true;
|
|
13
|
+
readonly source: "sealed";
|
|
14
|
+
readonly reason: SealedCustodyPoisonReason;
|
|
15
|
+
readonly publicKeyId: string;
|
|
16
|
+
readonly errKind?: string;
|
|
17
|
+
/** ISO timestamp of the apply that detected the failure. */
|
|
18
|
+
readonly at: string;
|
|
19
|
+
} | {
|
|
20
|
+
readonly modelKeyPoison: true;
|
|
21
|
+
readonly source: "env";
|
|
22
|
+
readonly reason: "env_missing";
|
|
23
|
+
readonly apiKeyEnv: string;
|
|
24
|
+
/** ISO timestamp of the resolve that detected the absence. */
|
|
25
|
+
readonly at: string;
|
|
26
|
+
};
|
|
27
|
+
/** `config.modelApiKeys` 里能植的那一种(env 来源的毒丸由链现判,不进表)。 */
|
|
28
|
+
export type SealedCustodyPoison = Extract<ModelKeyPoison, {
|
|
29
|
+
source: "sealed";
|
|
30
|
+
}>;
|
|
31
|
+
/** 铸毒丸(纯数据 ⇒ 加上标记与检测时刻)。重载让调用方拿到它那一种的窄型。 */
|
|
32
|
+
export declare function modelKeyPoison(p: {
|
|
33
|
+
source: "sealed";
|
|
34
|
+
reason: SealedCustodyPoisonReason;
|
|
35
|
+
publicKeyId: string;
|
|
36
|
+
errKind?: string;
|
|
37
|
+
}): SealedCustodyPoison;
|
|
38
|
+
export declare function modelKeyPoison(p: {
|
|
39
|
+
source: "env";
|
|
40
|
+
reason: "env_missing";
|
|
41
|
+
apiKeyEnv: string;
|
|
42
|
+
}): ModelKeyPoison;
|
|
43
|
+
export declare function isModelKeyPoison(v: unknown): v is ModelKeyPoison;
|
|
44
|
+
/** 毒丸的来源身份(托管 ⇒ `publicKeyId`;env ⇒ 变量名)—— 指纹与日志的读口,穷举 switch。 */
|
|
45
|
+
export declare function modelKeyPoisonIdentity(p: ModelKeyPoison): string;
|
|
46
|
+
/**
|
|
47
|
+
* The task-level fail-loud face of a poison pill: thrown by the key resolver on EVERY brain call that would otherwise
|
|
48
|
+
* authenticate this model, so an unusable declared key fails the task visibly instead of silently burning the gateway
|
|
49
|
+
* account. Carries the poison (diagnosis only) — nothing secret, by construction of {@link ModelKeyPoison}.
|
|
50
|
+
*/
|
|
51
|
+
export declare class ModelKeyPoisonedError extends Error {
|
|
52
|
+
readonly poison: ModelKeyPoison;
|
|
53
|
+
readonly reason: ModelKeyPoisonReason;
|
|
54
|
+
readonly at: string;
|
|
55
|
+
constructor(modelName: string, poison: ModelKeyPoison);
|
|
56
|
+
}
|
|
3
57
|
/**
|
|
4
58
|
* Build a per-model API-key resolver for `TaskSpec.getApiKeyAndHeaders` from the catalog's
|
|
5
59
|
* name → env-var-NAME map (sema-registry `apiKeyEnv`). core threads this per brain call — and per cascade
|
|
6
60
|
* rung — so each model/rung authenticates with its OWN upstream account/key.
|
|
7
61
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
62
|
+
* A model absent from BOTH maps returns `undefined`, so core falls back to the brain's construction-time
|
|
63
|
+
* gateway key — that is the operator's choice (the entry declared no key of its own). A model that DECLARED
|
|
64
|
+
* a key it cannot get (poisoned sealed key, or an `apiKeyEnv` whose variable is missing / empty in this
|
|
65
|
+
* process) THROWS {@link ModelKeyPoisonedError} — never `undefined` (S-896). When no per-model keys are
|
|
66
|
+
* configured at all this returns `undefined`, leaving the spec field unset so core's default path is
|
|
67
|
+
* byte-for-byte unchanged.
|
|
12
68
|
*
|
|
13
69
|
* Only the api KEY is per-model; the base URL stays brain-owned ([ref]) — core's seam carries
|
|
14
|
-
* `{ apiKey, headers? }`, not a baseUrl.
|
|
70
|
+
* `{ apiKey, headers? }`, not a baseUrl.
|
|
15
71
|
*
|
|
16
72
|
* `modelApiKeys` (sealed-box custody) carries per-model entries unsealed from `ModelEntry.sealedApiKey` —
|
|
17
|
-
* resolved FIRST (sealed outranks the env-NAME reference, the registry-core mutual-exclusion ruling
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* silently burn the shared gateway account (the core invariant of the poison-pill fix; a poisoned model
|
|
22
|
-
* skips its env reference too, same mutual-exclusion as a healthy sealed key). The maps are disjoint by
|
|
23
|
-
* construction in applyEffective (a sealed model never lands in modelApiKeyEnv); the ordering here is
|
|
24
|
-
* belt-and-suspenders. 🔴 Plaintext values are live secrets: memory-only, never log them.
|
|
73
|
+
* resolved FIRST (sealed outranks the env-NAME reference, the registry-core mutual-exclusion ruling; a
|
|
74
|
+
* poisoned sealed model skips its env reference too). The maps are disjoint by construction in
|
|
75
|
+
* applyEffective (a sealed model never lands in modelApiKeyEnv); the ordering here is belt-and-suspenders.
|
|
76
|
+
* 🔴 Plaintext values are live secrets: memory-only, never log them.
|
|
25
77
|
*/
|
|
26
78
|
/** The ONE field the per-model key plane keys on. The resolver's parameter is deliberately this minimal
|
|
27
79
|
* named ref (not the full core `Model`): full models are structural supersets and pass unchanged, while
|
|
@@ -29,12 +81,14 @@ import { type SealedKeyPoison } from "./sealed-key.js";
|
|
|
29
81
|
* can mint `{ name }` legally instead of forcing it through an `as never` escape hatch (B1). Contravariance
|
|
30
82
|
* keeps the resolver assignable wherever `(model: Model) => …` is expected. */
|
|
31
83
|
export type ModelKeyRef = Pick<Model, "name">;
|
|
32
|
-
export declare function createKeyResolver(modelApiKeyEnv: Record<string, string>, env?: NodeJS.ProcessEnv, modelApiKeys?: Record<string, string |
|
|
84
|
+
export declare function createKeyResolver(modelApiKeyEnv: Record<string, string>, env?: NodeJS.ProcessEnv, modelApiKeys?: Record<string, string | SealedCustodyPoison>): ((model: ModelKeyRef) => Promise<{
|
|
33
85
|
apiKey: string;
|
|
34
86
|
} | undefined>) | undefined;
|
|
87
|
+
/** 「这只模型声明的钥匙来源今天拿不到」⇒ 那一只毒丸;拿得到 / 没声明 ⇒ `undefined`。能力面的配对标注据它答 `"unknown"`(判别,不是 catch)。 */
|
|
88
|
+
export declare function modelKeyPoisonOf(modelName: string, modelApiKeyEnv: Record<string, string>, env?: NodeJS.ProcessEnv, modelApiKeys?: Record<string, string | SealedCustodyPoison>): ModelKeyPoison | undefined;
|
|
35
89
|
/**
|
|
36
90
|
* The ONE per-model key chain, synchronous: **sealed custody → env-NAME reference → (caller's) gateway key**,
|
|
37
|
-
* with a
|
|
91
|
+
* with a declared-but-unusable source THROWING instead of returning undefined. `createKeyResolver` is the async
|
|
38
92
|
* `getApiKeyAndHeaders` face of exactly this function — every other consumer must call it rather than
|
|
39
93
|
* re-implement a subset of the chain.
|
|
40
94
|
*
|
|
@@ -42,8 +96,9 @@ export declare function createKeyResolver(modelApiKeyEnv: Record<string, string>
|
|
|
42
96
|
* 降级目标(按互斥契约**永不**落 modelApiKeyEnv)于是静默拿网关 key 打自己的网关(错 key 打上游),毒丸更
|
|
43
97
|
* 被当成"没配 key"。链只有一条,重复实现就会长出这种半条链的偏差,故此处抽出同步内核共用。
|
|
44
98
|
*
|
|
45
|
-
* `undefined` = this model
|
|
46
|
-
* additive contract).
|
|
99
|
+
* `undefined` = this model DECLARED no per-model key ⇒ the CALLER's gateway/default credential applies (the
|
|
100
|
+
* additive contract). A declared env reference whose variable is missing or empty throws (S-896; 7.105.0 and
|
|
101
|
+
* earlier answered `undefined` there — the silent gateway fallback this family removes).
|
|
47
102
|
*/
|
|
48
|
-
export declare function resolveModelApiKey(modelName: string, modelApiKeyEnv: Record<string, string>, env?: NodeJS.ProcessEnv, modelApiKeys?: Record<string, string |
|
|
103
|
+
export declare function resolveModelApiKey(modelName: string, modelApiKeyEnv: Record<string, string>, env?: NodeJS.ProcessEnv, modelApiKeys?: Record<string, string | SealedCustodyPoison>): string | undefined;
|
|
49
104
|
//# sourceMappingURL=key-resolver.d.ts.map
|