@sema-agent/server 5.11.0 → 5.12.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/dist/http/server.js +8 -8
- package/dist/parked-decide.d.ts +6 -4
- package/dist/parked-decide.js +24 -8
- package/package.json +2 -2
package/dist/http/server.js
CHANGED
|
@@ -1245,9 +1245,9 @@ export function createHttpServer(rawDeps) {
|
|
|
1245
1245
|
...(binding !== undefined ? { binding: { ...(binding.boundCallId !== undefined ? { boundCallId: binding.boundCallId } : {}), ...(binding.boundInputHash !== undefined ? { boundInputHash: binding.boundInputHash } : {}), ...(binding.updatedInput !== undefined ? { updatedInput: binding.updatedInput } : {}) } } : {}),
|
|
1246
1246
|
// remember 的 grant 闭包(onResumeCommitted 的唯一现役来源)在 parked 腿以行的 root/host
|
|
1247
1247
|
// 会话为锚透传([1591] 候裁② server 修——落子代会话则探针键永不相交,见 grantOnCommit 注);
|
|
1248
|
-
// answer
|
|
1248
|
+
// answer 原样透传(RB-459,core 5.7.0 起 revive 侧有挂点;形状校验已在路由层 isQuestionAnswer)。
|
|
1249
1249
|
...(onResumeCommitted ? { grantRemember: (rootSessionId) => onResumeCommitted(rootSessionId) } : {}),
|
|
1250
|
-
...(answer !== undefined ? {
|
|
1250
|
+
...(answer !== undefined ? { answer } : {}),
|
|
1251
1251
|
});
|
|
1252
1252
|
if (parked !== undefined) {
|
|
1253
1253
|
void ctxPromise.catch(() => { }); // 并行 ctx 读不再被消费,吞掉可能的 rejection
|
|
@@ -1342,12 +1342,11 @@ export function createHttpServer(rawDeps) {
|
|
|
1342
1342
|
return { status: 400, body: { error: `body.answer is only valid when approving a pending AskUserQuestion (decision: "${decision}", pending: "${pendingTool ?? "unknown"}")`, errorCode: "request.field_conflict" } };
|
|
1343
1343
|
}
|
|
1344
1344
|
const { objective: resumeObjective, sessionId: _sessionId, ...taskConfig } = spec;
|
|
1345
|
-
// Durable ask (TC-5.4, core
|
|
1346
|
-
//
|
|
1347
|
-
//
|
|
1348
|
-
// answer ⇒
|
|
1349
|
-
|
|
1350
|
-
taskConfig.onQuestion = async () => answer;
|
|
1345
|
+
// Durable ask (TC-5.4 → RB-459, core 5.7.0): the operator's answer rides the ResumeOutcome — core's
|
|
1346
|
+
// answerFaceForRedeemedCall replays it to the REDEEMED question call only, so a NEW question on the
|
|
1347
|
+
// resumed leg gets an honest error instead of a silently replayed stale answer (which the pre-5.7.0
|
|
1348
|
+
// `onQuestion = async () => answer` closure form did). Absent answer ⇒ outcome unchanged (an F4 tool
|
|
1349
|
+
// approval resumes exactly as before). The onQuestion seat is left alone.
|
|
1351
1350
|
// D-1 decision-action binding (core 1.101, design/80 §2): echo the boundCallId + boundInputHash the operator
|
|
1352
1351
|
// saw back into the outcome — core verifies opaque STRING equality against the persisted suspend-mint values
|
|
1353
1352
|
// (fail-closed, PRE-CAS), so "saw action A, resolve B" is rejected. boundInputHash is NEVER recomputed here
|
|
@@ -1364,6 +1363,7 @@ export function createHttpServer(rawDeps) {
|
|
|
1364
1363
|
decision: decision === "approve" ? "allow" : "deny",
|
|
1365
1364
|
...(decision === "approve" && binding?.updatedInput !== undefined ? { updatedInput: binding.updatedInput } : {}),
|
|
1366
1365
|
...(reason ? { reason } : {}),
|
|
1366
|
+
...(answer !== undefined ? { answer } : {}),
|
|
1367
1367
|
};
|
|
1368
1368
|
// Developer-mode verify (1.44) survives suspend/resume (core `resumeWithVerification`, design/51 P1-b): a
|
|
1369
1369
|
// task submitted with verify:true that hit an F4 gate must STILL be gated by the adversarial verifier on
|
package/dist/parked-decide.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* 判别必须 parked-first:legacy 腿的 `resumeStream` 会绕开 bg registry(无 consumeParkedFlip、
|
|
9
9
|
* 行永 parked、生命周期分叉)。
|
|
10
10
|
*/
|
|
11
|
-
import type { BackgroundAgentRecord, BackgroundAgentStore, CheckpointStore, TaskRegistry, ToolSpec } from "@sema-agent/core";
|
|
11
|
+
import type { BackgroundAgentRecord, BackgroundAgentStore, CheckpointStore, QuestionAnswer, TaskRegistry, ToolSpec } from "@sema-agent/core";
|
|
12
12
|
export interface ParkedAgentMatch {
|
|
13
13
|
handle: string;
|
|
14
14
|
row: BackgroundAgentRecord;
|
|
@@ -72,9 +72,11 @@ export interface ParkedDecideRequest {
|
|
|
72
72
|
* root 会话则宿主键闭包立即命中,复活子代的后续同工具 ask 被短路(探针 v2 实证),语义与同步车道/
|
|
73
73
|
* 宿主任务的「本会话不再询问」一致(= host 会话树全域)。行无 root 锚(病态形)⇒ claim 前诚实 400。 */
|
|
74
74
|
grantRemember?: (rootSessionId: string) => Promise<void>;
|
|
75
|
-
/**
|
|
76
|
-
*
|
|
77
|
-
|
|
75
|
+
/** RB-459(core 5.7.0,F6 翻案):AskUserQuestion 的 approve 携操作员答案——原样透传进
|
|
76
|
+
* `ResumeOutcome.answer`(形状校验在路由层 isQuestionAnswer;header 逐字匹配等语义校验在 core,
|
|
77
|
+
* server 不预判)。core 拒绝形:同步腿 409 invalid_outcome field:"answer",parked 腿经 drive 失败
|
|
78
|
+
* 面 422 parked_resume.startup_failed。 */
|
|
79
|
+
answer?: QuestionAnswer;
|
|
78
80
|
/** claim CAS 赢了(≈ legacy 腿的 markResuming 赢)之后、execute 之前触发——remember 豁免 grant
|
|
79
81
|
* 的挂点(调用方自吞错,绝不抛落赎回)。claim 败/判别 miss 恒不触发。 */
|
|
80
82
|
onDecideCommitted?: () => Promise<void>;
|
package/dist/parked-decide.js
CHANGED
|
@@ -76,15 +76,13 @@ export async function decideParkedAgent(deps, req) {
|
|
|
76
76
|
// remember 的 grant 必须锚在 root/host 会话(探针闭包键);行无任何会话锚 = 无处可落,claim 前诚实拒。
|
|
77
77
|
return { status: 400, body: { error: "remember cannot be applied: this parked agent row carries no root/parent session anchor for the exemption", errorCode: "decide.parked_remember_unsupported", taskId: match.handle } };
|
|
78
78
|
}
|
|
79
|
-
if (req.answerPresent === true) {
|
|
80
|
-
// 复审 F6:与 legacy 腿同姿势显式拒,不静默丢弃操作员的输入。
|
|
81
|
-
return { status: 400, body: { error: "body.answer is not supported when deciding a parked background agent (no question hookpoint on the revive path)", errorCode: "decide.parked_answer_unsupported", taskId: match.handle } };
|
|
82
|
-
}
|
|
83
79
|
const pendingTool = req.pendingAction?.toolName;
|
|
84
|
-
if (pendingTool === "AskUserQuestion" && req.decision === "approve") {
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
|
|
80
|
+
if (pendingTool === "AskUserQuestion" && req.decision === "approve" && req.answer === undefined) {
|
|
81
|
+
// RB-459(core 5.7.0,F1/F6 翻案后唯一剩下的 claim 前拒):问题门的 approve 必须携 answer——
|
|
82
|
+
// parked 腿无 live answering face,core 侧同判(invalid_outcome field:"answer"),但那要消费 claim
|
|
83
|
+
// 并空跑一次 revive;这里 claim 前拒是同一判据的免损前置,不是 server 另立规则。带 answer 的
|
|
84
|
+
// approve 与任何 deny 都放行透传,core 是语义权威(header 逐字匹配等)。
|
|
85
|
+
return { status: 400, body: { error: "pending action is AskUserQuestion — approve requires body.answer (the operator's answers[]); deny needs none", errorCode: "decide.parked_answer_required", taskId: match.handle } };
|
|
88
86
|
}
|
|
89
87
|
if (row.name === undefined) {
|
|
90
88
|
// forwardDurableApproval 只对具名子代成立,parked 行理应恒有 name;缺 = 行受损,claim 前诚实拒
|
|
@@ -135,6 +133,8 @@ export async function decideParkedAgent(deps, req) {
|
|
|
135
133
|
boundInputHash: req.binding?.boundInputHash ?? persistedHash,
|
|
136
134
|
decision: req.decision === "approve" ? "allow" : "deny",
|
|
137
135
|
...(req.decision === "approve" && req.binding?.updatedInput !== undefined ? { updatedInput: req.binding.updatedInput } : {}),
|
|
136
|
+
// RB-459:answer 原样透传(核不预判、不改写——header 逐字匹配/合法组合全在 core 的 resume 校验)。
|
|
137
|
+
...(req.answer !== undefined ? { answer: req.answer } : {}),
|
|
138
138
|
// B10:存在性判定,不是真值判定 —— reason 可以是空串("运维显式选择不写理由"),这与"没传 reason"
|
|
139
139
|
// 是两件不同的事(姊妹 approval-hmac.ts `env.reason ?? null` 同判据:`??` 只在 null/undefined 时落
|
|
140
140
|
// null,空串照样入签名载荷)。真值判定会把显式 "" 与缺席折成同一个结果,审计/签名面丢了这个区分。
|
|
@@ -200,6 +200,22 @@ export async function decideParkedAgent(deps, req) {
|
|
|
200
200
|
// 无害 "lost"([1588] Q2 幂等核定),结果面仍诚实(操作员重查 pending 列表即可自愈)。
|
|
201
201
|
const disp = await rollback("execute_rejected");
|
|
202
202
|
const content = receipt.content;
|
|
203
|
+
const receiptError = receipt.details?.error;
|
|
204
|
+
if (receiptError === "parked_resume.startup_failed") {
|
|
205
|
+
// RB-459([2458] 裁定形):resume 已发起但启动即败(含 core 对 outcome.answer 的语义拒——
|
|
206
|
+
// header 不匹配/answer 配错门,invalid_outcome field:"answer" 在 drive 内抛)——422 透传 core
|
|
207
|
+
// 的码,不塌 409 revive_rejected(那是入参级早退的形)。core 已自滚,上面的补滚在 claimId CAS
|
|
208
|
+
// 下退化无害 lost(G-F5 口径),行回 parked 可再决。
|
|
209
|
+
return {
|
|
210
|
+
status: 422,
|
|
211
|
+
body: {
|
|
212
|
+
error: typeof content === "string" ? content : "the parked resume failed during startup",
|
|
213
|
+
errorCode: "parked_resume.startup_failed",
|
|
214
|
+
taskId: match.handle,
|
|
215
|
+
rollback: disp,
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
203
219
|
return {
|
|
204
220
|
status: 409,
|
|
205
221
|
body: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.12.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",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"build:binary:run-local:darwin-arm64": "bun build --compile --target=bun-darwin-arm64 src/run-local.ts --outfile dist/run-local-darwin-arm64"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@sema-agent/core": "^5.
|
|
57
|
+
"@sema-agent/core": "^5.7.0",
|
|
58
58
|
"@sema-agent/registry-core": "^0.14.0",
|
|
59
59
|
"e2b": "^2.28.0",
|
|
60
60
|
"libsodium-wrappers": "^0.8.4",
|