@sema-agent/server 7.18.0 → 7.19.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/route-ctx.d.ts +9 -0
- package/dist/http/server.js +43 -4
- package/package.json +2 -2
package/dist/http/route-ctx.d.ts
CHANGED
|
@@ -109,6 +109,15 @@ export interface DriveResumeArgs {
|
|
|
109
109
|
* ask sees them — the decide leg's exemption grant). Contract: must not throw (callers swallow internally);
|
|
110
110
|
* awaited so the ordering guarantee is real, and a store write here is ms-scale vs the model leg. */
|
|
111
111
|
onResumeCommitted?: () => Promise<void>;
|
|
112
|
+
/** [3829]/[3830]:调用方声明「本腿的 park 是终局后纯 park,session claim 已随终局 finalize 删除」——
|
|
113
|
+
* getActiveTaskId 缺席时**铸新 canonical taskId + createRun 全生命周期行**(wake 续跑=同 session 新一轮
|
|
114
|
+
* turn),而不是无行裸跑(不可取消/无计费归因/无 durable append/fleet 不可见,且 core `spec.taskId ??
|
|
115
|
+
* sessionId` fallback 会把 lastRunId 铸成 sessionId)。`source` 进 run 行元数据(runMeta 同列)。
|
|
116
|
+
* decide/answer/plan_review 腿**不设**此位:它们的 claim 在 park 期间特意保留,缺席=行被 reap 的竞态,
|
|
117
|
+
* 维持既有兜底(checkpoint token 同拍过期 ⇒ resumeStream 拒),不铸新行。 */
|
|
118
|
+
mintFreshRun?: {
|
|
119
|
+
source: string;
|
|
120
|
+
};
|
|
112
121
|
}
|
|
113
122
|
/** 提交/续跑「腿」= 跨域共享的**有状态**长流程(不像 {@link RouteHelpers} 那样只依赖 deps/config:它们要写
|
|
114
123
|
* run log、发 fleet 帧、走 markResuming CAS、驱动模型)。A9 上批的停点就在这里——tasks/runs 两域共用
|
package/dist/http/server.js
CHANGED
|
@@ -13,7 +13,7 @@ import { parseHooksConfig } from "../hooks/hook-runner.js";
|
|
|
13
13
|
import { normalizeApproachNotice, validateTaskAgents } from "../spec-fields.js";
|
|
14
14
|
import { isValidCwd, MAX_ADDITIONAL_DIRS } from "../task-cwd.js";
|
|
15
15
|
import { runInBackground, evictIfConflict, stripCheckpointToken, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort } from "../runs.js";
|
|
16
|
-
import { recordTurnActivity } from "../turn-activity.js";
|
|
16
|
+
import { readTurnActivityMs, recordTurnActivity } from "../turn-activity.js";
|
|
17
17
|
import { looksLikeJwt } from "@sema-agent/registry-core/api/auth-bridge";
|
|
18
18
|
import {} from "../orchestration/workflow-agent-steer.js";
|
|
19
19
|
import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKey, NotifiedKeys } from "../orchestration/workflow-completion-inbox.js";
|
|
@@ -61,7 +61,7 @@ export { coarseStatusForState, errorCodeForExit };
|
|
|
61
61
|
// (routes/* 绝不可值 import server.ts:那条边会闭合运行时环,见 test/module-cycle-gate.test.ts)。
|
|
62
62
|
import { sendJson, sendError, httpErrorCode, msg } from "./send.js";
|
|
63
63
|
import { authorized, systemFor, gatedPrincipal, explicitOperatorOk, isOperator } from "./principal-gate.js";
|
|
64
|
-
import { resumeEntryForGate } from "./active-run-conflict.js";
|
|
64
|
+
import { buildActiveRunConflict, resumeEntryForGate } from "./active-run-conflict.js";
|
|
65
65
|
export { explicitOperatorOk, isOperator };
|
|
66
66
|
/** 分组入参 → 平铺视图(createHttpServer 的第一件事)。一组都没给 ⇒ 原样返回(存量调用零开销、零形变)。 */
|
|
67
67
|
export function flattenServiceDeps(deps) {
|
|
@@ -1687,7 +1687,36 @@ export function createHttpServer(rawDeps) {
|
|
|
1687
1687
|
return { status: 429, body: { error: "budget exhausted (quota lease)", errorCode: "quota_exhausted", retryAfterSec: adm.retryAfterSec } };
|
|
1688
1688
|
}
|
|
1689
1689
|
}
|
|
1690
|
-
|
|
1690
|
+
let taskId = await deps.runStore?.getActiveTaskId(sessionId);
|
|
1691
|
+
// [3829]/[3830] wake 腿(mintFreshRun 声明位):task_done 纯 park 的终局 finalize 已删 task_active
|
|
1692
|
+
// claim——本腿在此恒拿不到 taskId,曾经整腿无行裸跑(不可取消/无计费归因/无 durable append/fleet
|
|
1693
|
+
// 不可见),而 core `spec.taskId ?? sessionId` fallback 还会把 lastRunId 铸成 sessionId(续听恒 404,
|
|
1694
|
+
// cli 7.18 提货围栏首撞)。wake 续跑=同 session 新一轮 turn(原任务已终局),正确形=铸新 canonical
|
|
1695
|
+
// taskId + createRun 全生命周期行;createRun 的 task_active 唯一键 claim 同时补上 double-text 互斥
|
|
1696
|
+
// (getActiveTaskId 读后另一提交抢建行的竞态窗 ⇒ 与 sync 腿同形 409 真出路)。未声明该位的腿
|
|
1697
|
+
// (decide/answer/plan_review)语义不变:claim 缺席=行被 reap 的竞态,checkpoint token 同拍过期兜底。
|
|
1698
|
+
let freshRunClaimed = false;
|
|
1699
|
+
// 🔴 codex R1-F1(2026-08-14):mintFreshRun 腿对「claim 有值」**只有 409 一条出路**,绝不落入下方的
|
|
1700
|
+
// markResuming 复用臂。本腿的 park 是终局后纯 park——它自己的行早已终局并释放 claim,此刻 claim 里
|
|
1701
|
+
// 的必是**别的 run**(典型:task_done park 之后同 session 又提交了新任务且其已 park 成 suspended,
|
|
1702
|
+
// claim 保留)。旧路径会用**本 park 的旧 token**对**对方的行**赢下 markResuming CAS 并驱动续跑——
|
|
1703
|
+
// 错误终局化对方的行、孤儿化对方仍 pending 的卡(checkpoint 表无 per-session pending 唯一约束,
|
|
1704
|
+
// findPendingTokenBySession 又是无序 LIMIT 1,两条 pending 并存时旧 task_done park 完全可能被选中)。
|
|
1705
|
+
if (args.mintFreshRun && taskId && deps.runStore) {
|
|
1706
|
+
return { status: 409, body: await buildActiveRunConflict({ runStore: deps.runStore, checkpointStore: deps.checkpointStore, governance: deps.config, turnActivity: readTurnActivityMs }, sessionId, taskId) };
|
|
1707
|
+
}
|
|
1708
|
+
if (!taskId && args.mintFreshRun && deps.runStore) {
|
|
1709
|
+
const freshId = uuidv7();
|
|
1710
|
+
const created = await deps.runStore.createRun(freshId, sessionId, principal ?? null, deps.instanceId ?? "default", {
|
|
1711
|
+
source: args.mintFreshRun.source,
|
|
1712
|
+
objectivePreview: resumeObjective ? redactSecrets(resumeObjective).slice(0, 120) : null,
|
|
1713
|
+
});
|
|
1714
|
+
if (!created.ok) {
|
|
1715
|
+
return { status: 409, body: await buildActiveRunConflict({ runStore: deps.runStore, checkpointStore: deps.checkpointStore, governance: deps.config, turnActivity: readTurnActivityMs }, sessionId, created.activeTaskId) };
|
|
1716
|
+
}
|
|
1717
|
+
taskId = freshId;
|
|
1718
|
+
freshRunClaimed = true;
|
|
1719
|
+
}
|
|
1691
1720
|
// MF-Fleet (TOC review #7): a HITL resume (/decide, /answer, /plan_review, /preempt-resume, /wake) drives the
|
|
1692
1721
|
// SAME durable run row that suspended — so it must re-publish to the fleet bus under the SAME runId, or the run
|
|
1693
1722
|
// VANISHES from GET /v1/fleet/stream the moment it resumes (the original publisher's row was last touched at
|
|
@@ -1773,7 +1802,9 @@ export function createHttpServer(rawDeps) {
|
|
|
1773
1802
|
// approval (and released the session lock), the run is terminal, or a concurrent resume already claimed
|
|
1774
1803
|
// it. Bail rather than run a resume whose run row is gone / a sibling owns; the checkpoint reaper expires
|
|
1775
1804
|
// the token in the same tick so resumeStream would reject anyway. Closes the window the heartbeat can't.
|
|
1776
|
-
|
|
1805
|
+
// [3829]:fresh 行由上面的 createRun 事务铸出即 `running` + claim 在手——markResuming 的 CAS 只认
|
|
1806
|
+
// suspended/needs_review,对它恒败;跳过=行本来就是本腿自己的,资格等价于 CAS 赢。
|
|
1807
|
+
claimedRow = freshRunClaimed ? true : await deps.runStore.markResuming(taskId);
|
|
1777
1808
|
if (!claimedRow)
|
|
1778
1809
|
return { status: 409, body: { error: "run is not in a resumable (suspended) state (already resumed, decided, or expired)", errorCode: "conflict.not_resumable" } };
|
|
1779
1810
|
// stoppedBy — resume leg NOW marked (core 1.256 delivered the mark-by-owner seam): codex F3(2026-08-13)
|
|
@@ -2377,6 +2408,13 @@ export function createHttpServer(rawDeps) {
|
|
|
2377
2408
|
// C: a plain Error (not CheckpointError, not a cancel) is about to propagate before the post-catch
|
|
2378
2409
|
// `settleFleet(result.status)` can run → settle the re-published row to "failed" first so it doesn't leak
|
|
2379
2410
|
// as a stale "running" (no-op if a CheckpointError/cancel branch above already settled it).
|
|
2411
|
+
// 🔴 codex R1-F2(2026-08-14):行也要落终局,不只 fleet 帧——本腿在 markResuming/createRun 赢下行后
|
|
2412
|
+
// 普通异常一路 throw 出去,行曾一直留 running+claim 在手直到 reapStale(期间 session 被锁死,最终被
|
|
2413
|
+
// 误记 instance-lost)。cancel 分支(上方)早为同一理由落行,本臂是漏掉的孪生。claimedRow 守卫=只有
|
|
2414
|
+
// 抢到行的腿才有资格驱终局(与 CheckpointError 臂同一条纪律)。
|
|
2415
|
+
if (claimedRow && taskId && deps.runStore) {
|
|
2416
|
+
await deps.runStore.setTerminal(taskId, "failed", null, e instanceof Error ? e.message : String(e)).catch(() => undefined);
|
|
2417
|
+
}
|
|
2380
2418
|
settleFleet("failed");
|
|
2381
2419
|
throw e;
|
|
2382
2420
|
}
|
|
@@ -2555,6 +2593,7 @@ export function createHttpServer(rawDeps) {
|
|
|
2555
2593
|
resumeObjective,
|
|
2556
2594
|
outcome: { gate: "wake", ...(message !== undefined ? { message: { text: message, trusted } } : {}) },
|
|
2557
2595
|
verifyRounds: undefined, // wake 续跑不重开 verify 门(原任务已终局;新工作要 verify 走新 run)
|
|
2596
|
+
mintFreshRun: { source: "wake" }, // [3829]:终局纯 park 的 claim 已删,声明铸新行(注释见 DriveResumeArgs)
|
|
2558
2597
|
});
|
|
2559
2598
|
}
|
|
2560
2599
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.19.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.31.0",
|
|
58
58
|
"@sema-agent/registry-core": "^0.16.0",
|
|
59
59
|
"e2b": "^2.28.0",
|
|
60
60
|
"libsodium-wrappers": "^0.8.4",
|