@sema-agent/server 5.3.0 → 5.4.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.
@@ -89,10 +89,8 @@ export function createResolveSpec(ctx) {
89
89
  // resolving here, skills/mcp would still be filtered by the raw unknown name → any skill/MCP tagged
90
90
  // ["default"] silently drops. council finding.)
91
91
  const scenarioName = scenarios[requested] ? requested : "default";
92
- // [891] deprecation(一版过渡窗):"autonomous" 已改名 "code"(persona 同一,唯一差别=alias
92
+ // [891]→[2400]:"autonomous" alias 已退役(clay 裁不留过渡)——body 显式传它走 scenario_unknown 400。
93
93
  // 定死 finalVerification:true 保存量行为)。warn 不 400——TB harness/存量部署平滑迁移。
94
- if (scenarioName === "autonomous")
95
- logger.warn("scenario_autonomous_deprecated", { alias: "code", note: "renamed by [891]; alias keeps finalVerification pinned — pass scenario:'code' (+ explicit finalVerification if wanted)" });
96
94
  const cap = selectScenario(scenarios, scenarioName)(body, auth?.principal);
97
95
  // codex R3/R4 (appendSystemPrompt 案): ONE center-pack snapshot per request — the append-less gate below
98
96
  // AND the provider pick (promptProvider field) both read THIS capture, never the mutable centerPrompts ref
@@ -538,7 +536,7 @@ export function createResolveSpec(ctx) {
538
536
  // design/132 (core 1.249): one-shot end-game verification nudge ("re-run the final artifact through
539
537
  // its real entrypoint before finishing"). OPT-IN by core's own judgment (default ON lost the evidence case:
540
538
  // +1 turn on every interactive write task) — the AUTONOMY caller declares it (harness/scheduler lanes).
541
- // [849] scenario-declared leg: the `autonomous` scenario pins it in its bundle (cap.finalVerification —
539
+ // [849]→[2400] scenario-declared leg 已无生产者(autonomous 退役后无场景定死终验);保留 OR 折入形防回归 —— (cap.finalVerification —
542
540
  // picking that scenario IS the autonomy declaration, so this is not "inferring from scenario"); OR-folded
543
541
  // with the caller's explicit flag — a scenario can only ADD the closing verification turn, never strip a
544
542
  // caller's request. Children do not inherit (core semantics).
@@ -19,7 +19,7 @@ export interface ScenarioBundle {
19
19
  skills: SkillSpec[];
20
20
  promptProvider?: PromptProvider;
21
21
  /** [849] 场景解释层定死的终验开关(core TaskSpec.finalVerification):bench 实测产品默认请求与
22
- * bench 态差距三件之一。只有 `autonomous` true;spec 组装处与 caller 显式声明取 OR(场景只会
22
+ * bench 态差距三件之一。[2400] 起无场景定死(autonomous 退役)——纯 caller 显式旋钮;spec 组装处 OR 折入(场景只会
23
23
  * 多加一轮收官验证,绝不收窄 caller 意图),壳/caller 不感知。 */
24
24
  finalVerification?: true;
25
25
  }
@@ -130,7 +130,6 @@ export declare function semaIdentityProvider(): PromptProvider;
130
130
  */
131
131
  export declare function codePromptProvider(brand: boolean): PromptProvider;
132
132
  /** @deprecated [891] 改名 code——一版过渡期保留旧符号(与场景名 alias 同窗退役)。 */
133
- export declare const autonomousPromptProvider: typeof codePromptProvider;
134
133
  export declare function buildScenarios(deps: ScenarioDeps): Record<string, Scenario>;
135
134
  /** Resolve the scenario for a request (falls back to `default`). */
136
135
  export declare function selectScenario(scenarios: Record<string, Scenario>, name: string | undefined): Scenario;
@@ -148,7 +147,7 @@ export interface ScenarioDetail {
148
147
  promptSummary: string;
149
148
  enabled: boolean;
150
149
  }
151
- /** 内建五场景详情。default/autonomous/team 工厂对良性请求无副作用可真调(拿真实工具名单);code-review/scan 是
150
+ /** 内建五场景详情。default/code/team 工厂对良性请求无副作用可真调(拿真实工具名单);code-review/scan 是
152
151
  * fail-loud 语义(缺 GIT_API 配置/principal 即 throw)→ 探针失败落静态表兜底(表↔工厂一致性由测试锁:
153
152
  * 测试喂 fake deps 真调工厂对账工具名)。enabled 对内建恒 true(约定②)。 */
154
153
  export declare function builtinScenarioDetails(scenarios: Record<string, Scenario>, deps: Pick<ScenarioDeps, "brandIdentity">): Record<string, ScenarioDetail>;
@@ -86,12 +86,12 @@ export function codePromptProvider(brand) {
86
86
  };
87
87
  }
88
88
  /** @deprecated [891] 改名 code——一版过渡期保留旧符号(与场景名 alias 同窗退役)。 */
89
- export const autonomousPromptProvider = codePromptProvider;
89
+ // [2400] CAPS-OPS-11:autonomousPromptProvider 别名导出已随场景退役删除(真身=codePromptProvider)。
90
90
  export function buildScenarios(deps) {
91
91
  // The default scenario filters skills by "default" — NOT by req.scenario (which may be an unknown name
92
92
  // that fell back here): the bundle must be a coherent "default" set, matching how main.ts resolves an
93
93
  // unknown scenario to "default" for tools/prompt/mcp too (council finding — else default-tagged drop).
94
- // 抽成具名工厂:`autonomous` 直接复用它取 bundle(工具/技能面与 default 恒等由构造保证,不靠两处
94
+ // 抽成具名工厂:`code` 直接复用它取 bundle(工具/技能面与 default 恒等由构造保证,不靠两处
95
95
  // 各写一份 assembleCodeTools 参数再祈祷不漂移)。
96
96
  const defaultScenario = (req) => {
97
97
  // gap-audit rank-1 BLOCKER: the default agent is the CC agent loop — it MUST have the
@@ -176,14 +176,9 @@ export function buildScenarios(deps) {
176
176
  ...defaultScenario(req, principal),
177
177
  promptProvider: codePromptProvider(deps.brandIdentity === true),
178
178
  }),
179
- // [891] 兼容 alias(一版过渡,deprecation warn 在 main.ts 解析处):旧语义逐字保留 —— code persona
180
- // + finalVerification 场景层定死 true(存量 TB harness/部署传 "autonomous" 的行为一根毛不变;
181
- // 新调用方要终验请显式传 body.finalVerification)。
182
- autonomous: (req, principal) => ({
183
- ...defaultScenario(req, principal),
184
- promptProvider: codePromptProvider(deps.brandIdentity === true),
185
- finalVerification: true,
186
- }),
179
+ // [891]→[2400] CAPS-OPS-11(clay 2026-08-03,不留任何过渡):`autonomous` 过渡 alias 已退役。
180
+ // body 显式传它 5.1.0 起的 scenario_unknown 400 响亮拒(附现役词表);旧语义迁移形=
181
+ // scenario:"code" + body.finalVerification:true(终验自此=纯 caller 显式旋钮,场景层无定死)。
187
182
  "code-review": (req) => codeReview(deps, req),
188
183
  // Neutral repo-readonly scenario (OA ask): the SAME clone-free read-only repo tools as code-review
189
184
  // (repo_tree / repo_read_file / repo_pull_diff over GIT_API_TOKEN) with NO promptProvider — the
@@ -228,7 +223,7 @@ export function buildScenarios(deps) {
228
223
  export function selectScenario(scenarios, name) {
229
224
  return (name && scenarios[name]) || scenarios.default;
230
225
  }
231
- /** 内建五场景详情。default/autonomous/team 工厂对良性请求无副作用可真调(拿真实工具名单);code-review/scan 是
226
+ /** 内建五场景详情。default/code/team 工厂对良性请求无副作用可真调(拿真实工具名单);code-review/scan 是
232
227
  * fail-loud 语义(缺 GIT_API 配置/principal 即 throw)→ 探针失败落静态表兜底(表↔工厂一致性由测试锁:
233
228
  * 测试喂 fake deps 真调工厂对账工具名)。enabled 对内建恒 true(约定②)。 */
234
229
  export function builtinScenarioDetails(scenarios, deps) {
@@ -247,9 +242,6 @@ export function builtinScenarioDetails(scenarios, deps) {
247
242
  ? "Sema 产品身份 + core 编码完全体提示词(CODE_AGENT persona+自治自检;CC 血统对表)"
248
243
  : "core 编码完全体提示词(CODE_AGENT persona+自治自检;CC 血统对表)"),
249
244
  default: mk("default", "通用/助理场景(域中性 persona,CC agent loop 全量工具面;[891] 起非出厂缺省,显式可选)", "full-body", probe("default", {}, undefined, ["Now"]), deps.brandIdentity ? "Sema 产品身份 + core 默认宪法(结构层由引擎合成)" : "core 默认宪法(中性,无场景附加 persona)"),
250
- autonomous: mk("autonomous", "⚠️ deprecated([891] 改名 code,一版过渡 alias):code persona + finalVerification 固定开(旧语义逐字保留)", "full-body", probe("autonomous", {}, undefined, ["Now"]), deps.brandIdentity
251
- ? "Sema 产品身份 + core 编码完全体提示词(= code 场景;finalVerification 固定开)"
252
- : "core 编码完全体提示词(= code 场景;finalVerification 固定开)"),
253
245
  "code-review": mk("code-review", "代码评审(clone-free 只读仓库工具;council=true 升多镜头评审团)", "repo-readonly", probe("code-review", { repo: "probe/probe" }, undefined, REPO_TOOLS), "评审员/评审团协调提示词(镜头分工+产出契约)"),
254
246
  scan: mk("scan", "中性只读仓库扫描(输出契约由 objective+skills 决定)", "repo-readonly", probe("scan", { repo: "probe/probe" }, undefined, REPO_TOOLS), "无场景附加提示词(objective+center skills 拥有输出契约)"),
255
247
  team: mk("team", "多智能体团队圆桌(模板成员+synthesizer,模板可由 registry 配置)", "team", probe("team", {}, undefined, ["run_team"]), "团队协调者提示词(圆桌纪律+合成流程)"),
@@ -718,7 +718,15 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
718
718
  return;
719
719
  }
720
720
  const lim = limRaw === null ? 20 : Number(limRaw);
721
- const page = keys.slice(0, lim);
721
+ // [2400] CAPS-OPS-6(clay 裁 2026-08-03,最优设计不留旧语义):
722
+ // · `?before=<key>`(exclusive)游标——keys 是 uuidv7 字典序 desc(新→旧),before 取更旧段;
723
+ // · `total` = listKeys 口径的**快照总数**(与 tree 腿的 total 同名同义;此前=页内行数,同名反义
724
+ // 且超过 limit 的快照永远取不到)。竞态窗口内正被 reap 的 ghost 键会短暂多计 1,下一拍自愈;
725
+ // · `nextBefore` 仅在还有更旧的 key 时给(=本页最后一个 key);
726
+ // · 复审 A-MED 的保护不变:latest 与**展示行**同源(ghost 不当 latest)、页行掉 ghost 不空报。
727
+ const beforeRaw = q.get("before");
728
+ const scoped = beforeRaw !== null ? keys.filter((k) => k < beforeRaw) : keys;
729
+ const page = scoped.slice(0, lim);
722
730
  const snapshots = [];
723
731
  for (const k of page) {
724
732
  const m = await fs.exportManifest(wsSession, k);
@@ -733,14 +741,14 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
733
741
  }
734
742
  snapshots.push({ key: k, files: m.size, ...(bytes !== undefined ? { bytes } : {}) });
735
743
  }
736
- // 🔴 复审 A-MED:`latest` `total` 必须与**展示行**同源——listKeys exportManifest 之间
737
- // reap 的键(ghost)若恰是字典序最大者,原写法会让 latest 指向已消失的快照(latest 别名的
738
- // tree/file/archive 全 404,尽管有有效的次新快照),且 total 计入 ghost 与 snapshots[] 不一致。
744
+ const lastPageKey = page.length > 0 ? page[page.length - 1] : undefined;
745
+ const hasMore = lastPageKey !== undefined && scoped.length > page.length;
739
746
  sendJson(res, 200, {
740
747
  sessionId: wsSession,
741
748
  snapshots,
742
749
  ...(snapshots.length > 0 ? { latest: snapshots[0].key } : {}),
743
- total: snapshots.length,
750
+ total: keys.length,
751
+ ...(hasMore ? { nextBefore: lastPageKey } : {}),
744
752
  });
745
753
  return;
746
754
  }
@@ -51,6 +51,7 @@ export { coarseStatusForState, errorCodeForExit };
51
51
  // (routes/* 绝不可值 import server.ts:那条边会闭合运行时环,见 test/module-cycle-gate.test.ts)。
52
52
  import { sendJson, sendError, httpErrorCode, msg } from "./send.js";
53
53
  import { authorized, systemFor, gatedPrincipal, explicitOperatorOk, isOperator } from "./principal-gate.js";
54
+ import { resumeEntryForGate } from "./active-run-conflict.js";
54
55
  export { explicitOperatorOk, isOperator };
55
56
  /** 分组入参 → 平铺视图(createHttpServer 的第一件事)。一组都没给 ⇒ 原样返回(存量调用零开销、零形变)。 */
56
57
  export function flattenServiceDeps(deps) {
@@ -1194,11 +1195,32 @@ export function createHttpServer(rawDeps) {
1194
1195
  // D-D SLA deny-sweep 不受影响:它的取行查询本就只选 gate_kind IN ('human','irreversible_ask')。
1195
1196
  const decideGateKind = cp.gate?.kind;
1196
1197
  if (decideGateKind !== "human" && decideGateKind !== "irreversible_ask") {
1198
+ // [2400] HITL-6(clay 裁 2026-08-03,不做兼容):补机读指路——与 [2255]① 的 409 材料同形
1199
+ // (pendingGate{kind, decidePath},resumeEntryForGate 单源表),客户端凭 decidePath 直达对的
1200
+ // resume 入口,不再解析人话文案。taskId 源=cp.sourceTaskId;缺席 ⇒ 只带 kind,诚实不铸假路径。
1201
+ const gnPath = decideGateKind && cp.sourceTaskId
1202
+ ? resumeEntryForGate(decideGateKind, { sessionId, taskId: cp.sourceTaskId })
1203
+ : null;
1197
1204
  return {
1198
1205
  status: 409,
1199
1206
  body: {
1200
1207
  error: `task is suspended on a '${decideGateKind ?? "unknown"}' gate, not a tool approval — a plan_review gate is resolved via POST /v1/assistant/tasks/:id/plan_review, a resource_limit suspension via POST /v1/assistant/tasks/:id/resume`,
1201
1208
  errorCode: "gate_not_tool_approval",
1209
+ ...(decideGateKind ? { pendingGate: { kind: decideGateKind, ...(gnPath ? { decidePath: gnPath } : {}) } } : {}),
1210
+ },
1211
+ };
1212
+ }
1213
+ // [2400] HITL-12(clay 裁 2026-08-03:不兼容老客户端):HTTP 腿强制决策-动作绑定——无 checkpointToken
1214
+ // 且无 boundCallId 的 decide 拒收(此前静默回落 current-pending:并发窗口下 A 想批的门刚被 B 的新门顶掉
1215
+ // 就裁错对象)。内部 D-D SLA deny-sweep(req 缺席)豁免:系统腿 deny 过期行,无并发歧义。位置刻意在
1216
+ // gate-kind 守卫**之后**:错门型先得 409+pendingGate 指路(HITL-6),而不是被本门的 400 遮蔽。
1217
+ if (req !== undefined && !binding?.checkpointToken && !binding?.boundCallId) {
1218
+ return {
1219
+ status: 400,
1220
+ body: {
1221
+ error: "decide requires the decision-action binding — echo checkpointToken and/or boundCallId from the pending approval (GET /v1/approvals)",
1222
+ errorCode: "decide_requires_binding",
1223
+ bindingEnforced: true,
1202
1224
  },
1203
1225
  };
1204
1226
  }
@@ -1979,6 +2001,9 @@ export function createHttpServer(rawDeps) {
1979
2001
  status: 200,
1980
2002
  body: {
1981
2003
  taskId, sessionId, status: result.status,
2004
+ // [2400] HITL-12:世代探测位——本 server 强制决策-动作绑定(无绑定 decide 已在上方 400 拒),
2005
+ // 客户端凭它机器确认「我裁的就是我看到的那个动作」,不再有静默回落窗口。
2006
+ bindingEnforced: true,
1982
2007
  ...(result.errorCode ? { errorCode: result.errorCode } : {}),
1983
2008
  ...(result.errorMessage ? { errorMessage: redactSecrets(result.errorMessage) } : {}),
1984
2009
  ...(checkpointReopened ? { retriable: true } : {}), // the park is still pending — re-fetch + re-decide
package/dist/run-local.js CHANGED
@@ -466,7 +466,7 @@ export async function runLocal(argv, deps = {}) {
466
466
  tools: cap.tools,
467
467
  skills: cap.skills,
468
468
  ...(cap.promptProvider ? { promptProvider: cap.promptProvider } : {}),
469
- // [849] 场景层定死的终验(autonomous 场景)——与 main.ts resolveSpec 同语义,CLI 路径不漂移。
469
+ // [849]→[2400] 场景层定死终验已无生产者(autonomous 退役);OR 折入形保留,与 resolveSpec 同语义。
470
470
  ...(cap.finalVerification === true ? { finalVerification: true } : {}),
471
471
  ...(mcp ? { mcp } : {}),
472
472
  ...(timeoutSec !== undefined ? { limits: { timeoutSec } } : {}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "5.3.0",
3
+ "version": "5.4.0",
4
4
  "description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -68,7 +68,7 @@
68
68
  "sharp": "^0.35.3"
69
69
  },
70
70
  "devDependencies": {
71
- "@sema-agent/sdk": "^4.2.0",
71
+ "@sema-agent/sdk": "^5.1.0",
72
72
  "@types/libsodium-wrappers": "^0.7.14",
73
73
  "@types/node": "22.10.2",
74
74
  "@types/pg": "^8.20.0",