@sema-agent/server 6.5.0 → 6.6.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/boot/resolve-spec.js +13 -2
- package/dist/http/server.js +18 -0
- package/dist/leader/wire.d.ts +5 -1
- package/dist/tool-approval.js +10 -9
- package/package.json +3 -3
|
@@ -280,6 +280,15 @@ export function createResolveSpec(ctx) {
|
|
|
280
280
|
if (rawAddDirs !== undefined && !cwdHonored(config)) {
|
|
281
281
|
logger.warn("task_additional_directories_ignored", { honored: false, sessionId: auth?.sessionId ?? null });
|
|
282
282
|
}
|
|
283
|
+
// core 5.11.0([2625] Added①,[2597] P0 案三决③A):`additionalReadDirectories` —— 只读围栏拓宽。
|
|
284
|
+
// 与 additionalDirectories 同门(host 单用户车道)、同形校验(parseAdditionalDirectories:绝对路径
|
|
285
|
+
// 数组 fail-loud)、同「忽略必留声」纪律;差异只在语义:只进 READ 面(classify 读边界 auto-allow +
|
|
286
|
+
// read_file/grep 等 containment),写面照旧拒——读授权永不静默变写授权(core 侧保证,我们只透传)。
|
|
287
|
+
const rawAddReadDirs = body.additionalReadDirectories;
|
|
288
|
+
const additionalReadDirectories = cwdHonored(config) ? parseAdditionalDirectories(rawAddReadDirs) : undefined;
|
|
289
|
+
if (rawAddReadDirs !== undefined && !cwdHonored(config)) {
|
|
290
|
+
logger.warn("task_additional_read_directories_ignored", { honored: false, sessionId: auth?.sessionId ?? null });
|
|
291
|
+
}
|
|
283
292
|
// body.model (explicit /model picker or run --model) WINS over settings.model (the config default); both gate
|
|
284
293
|
// against the catalog allow-list via resolveTaskModel (a settings.model not in the catalog is ignored, never injected).
|
|
285
294
|
// 🔴 TOB 档位联跑曾 FAIL(坐标级):the gate must see the TIER-EXPANDED catalog — expandTiers
|
|
@@ -320,7 +329,7 @@ export function createResolveSpec(ctx) {
|
|
|
320
329
|
metrics.inc("images_omitted_total", { model: picked.model });
|
|
321
330
|
logger.warn("images_omitted_no_vision_model", { model: picked.model, count: body.images.length });
|
|
322
331
|
}
|
|
323
|
-
return { taskHooks, additionalDirectories, wireCatalog, picked };
|
|
332
|
+
return { taskHooks, additionalDirectories, additionalReadDirectories, wireCatalog, picked };
|
|
324
333
|
};
|
|
325
334
|
/** 阶段③(anchors):吃 请求体 + auth,吐 两个历史锚解析后的 entryId(resumeAt / rewindFilesTo —— eventId→entryId
|
|
326
335
|
* 过 anchor store,未知锚在此 4xx 而不是交给 core)与 142-S4 项目登记簿种子 s4DefaultScopes。 */
|
|
@@ -374,7 +383,7 @@ export function createResolveSpec(ctx) {
|
|
|
374
383
|
/** 阶段④(spec 字面量):吃 请求体 + auth + `opts.leg` + 前三阶段的全部产出,吐**尚未过治理层**的 TaskSpec
|
|
375
384
|
* 字面量 —— 部署方拥有的 body→TaskSpec 映射本体(compactionModel 的 fresh 腿 400 也在这一段)。 */
|
|
376
385
|
const assembleSpecLiteral = (body, auth, opts, parts) => {
|
|
377
|
-
const { scenarioName, cap, centerDecls, acceptedAppend, parsedSettings, attachmentNotice, taskHooks, additionalDirectories, wireCatalog, picked, resumeAtEntryId, rewindFilesToEntryId, s4DefaultScopes, } = parts;
|
|
386
|
+
const { scenarioName, cap, centerDecls, acceptedAppend, parsedSettings, attachmentNotice, taskHooks, additionalDirectories, additionalReadDirectories, wireCatalog, picked, resumeAtEntryId, rewindFilesToEntryId, s4DefaultScopes, } = parts;
|
|
378
387
|
const spec = {
|
|
379
388
|
// D-1:附件告知随 objective 进 durable 流(只有名字/mime/尺寸——**内容永不进流**,这正是
|
|
380
389
|
// 「拼进 objective 是伪方案」的账要划清的线;文件名已消毒为安全字符集,无注入面)。
|
|
@@ -464,6 +473,8 @@ export function createResolveSpec(ctx) {
|
|
|
464
473
|
// design/119 (CC --add-dir): extra host dirs the FILE tools may access (core canonicalizes each into the
|
|
465
474
|
// containment allowlist + lists them in the `# Environment` block). Single-user host lane only (gated above).
|
|
466
475
|
additionalDirectories,
|
|
476
|
+
// core 5.11.0:只读围栏(见上方 parse 段注)。缺席=字节等价([2625] 自查③)。
|
|
477
|
+
additionalReadDirectories,
|
|
467
478
|
// EnterPlanMode (core 1.167): MODEL-DRIVEN plan — `enablePlanMode:true` alone (writable
|
|
468
479
|
// start, NO handsReadOnly) auto-mounts `enter_plan_mode`/`present_plan` so the model can self-enter read-only
|
|
469
480
|
// plan AT ANY TIME it judges a task needs planning. 🆕 DEFAULT ON (clay: resident; PLAN_MODE_ENABLED=false opts
|
package/dist/http/server.js
CHANGED
|
@@ -691,6 +691,24 @@ export function createHttpServer(rawDeps) {
|
|
|
691
691
|
return null;
|
|
692
692
|
}
|
|
693
693
|
}
|
|
694
|
+
// core 5.11.0:additionalReadDirectories(只读围栏)—— 与 additionalDirectories 同形 fail-loud
|
|
695
|
+
// (绝对路径数组、无 `..`、同条数上限);语义差异(只进 READ 面)在 core,形校验在此对齐。
|
|
696
|
+
const addReadDirsRaw = body.additionalReadDirectories;
|
|
697
|
+
if (addReadDirsRaw !== undefined) {
|
|
698
|
+
if (!Array.isArray(addReadDirsRaw)) {
|
|
699
|
+
sendError(res, 400, "request.field_invalid", "additionalReadDirectories must be an array of absolute host paths");
|
|
700
|
+
return null;
|
|
701
|
+
}
|
|
702
|
+
if (addReadDirsRaw.length > MAX_ADDITIONAL_DIRS) {
|
|
703
|
+
sendError(res, 400, "request.field_invalid", `additionalReadDirectories must have at most ${MAX_ADDITIONAL_DIRS} entries`);
|
|
704
|
+
return null;
|
|
705
|
+
}
|
|
706
|
+
const badRead = addReadDirsRaw.find((d) => !isValidCwd(d));
|
|
707
|
+
if (badRead !== undefined) {
|
|
708
|
+
sendError(res, 400, "request.field_invalid", "each additionalReadDirectories entry must be a non-empty absolute host path (no '..' segments)");
|
|
709
|
+
return null;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
694
712
|
// body.settings (client SemaSettings stamp) — fail-loud shape/size caps on submit, matching the sibling
|
|
695
713
|
// fields' posture (parseTaskSettings is also defensive on every path incl. resume; this 400s a bad submit early
|
|
696
714
|
// rather than silently coercing — adversarial review). The semantic projection (tighten-only) happens in resolveSpec.
|
package/dist/leader/wire.d.ts
CHANGED
|
@@ -131,7 +131,11 @@ export declare function leaderResourceConfig(env?: NodeJS.ProcessEnv): {
|
|
|
131
131
|
/** design/74 Slice 6: opt-in (LEADER_RESOURCE_SUSPEND=true) resource-slice suspend for workers. When set, a
|
|
132
132
|
* worker SUSPENDS at the per-slice window (`sliceMaxCostUsd`) instead of failing at the budget, and the
|
|
133
133
|
* leader auto-resumes (resumeWithVerification) until the TOTAL (`totalBudgetUsd` = workerBudgetUsd) is spent
|
|
134
|
-
* — a budget overrun becomes a resumable pause, not a worker failure.
|
|
134
|
+
* — a budget overrun becomes a resumable pause, not a worker failure. ⚠️ 语义更新(core 5.11.0 B3,
|
|
135
|
+
* 2026-08-06):`maxSuspends` 现在只数「连续无进展」park(批准+执行后链基归零),不再是总 park 数上限
|
|
136
|
+
* ——下面按预算切片总数放大的 sizing 公式因此从「必要」变「保守冗余」(合法的资源续跑链永不触 cap,
|
|
137
|
+
* cap 只剩防空转环职责);公式保留是无害的(上限只会更松不误杀),别再按「总数预算」理解它。
|
|
138
|
+
* 原注:`maxSuspends` is sized to the slice
|
|
135
139
|
* count (+margin) so the suspend-loop cap doesn't fire before the budget is legitimately spent. */
|
|
136
140
|
resourceSuspend?: {
|
|
137
141
|
totalBudgetUsd: number;
|
package/dist/tool-approval.js
CHANGED
|
@@ -382,18 +382,19 @@ export class ToolApprovalCoordinator {
|
|
|
382
382
|
// [1559]三:emit 完成 vs TTL/abort/respond 三选一落定——显式 race,不让挂死的 emit 独占执行权。
|
|
383
383
|
await Promise.race([emitAllP, allowedP]);
|
|
384
384
|
const settled = await allowedP;
|
|
385
|
-
// Completion breadcrumb (card dismiss) — fire-and-forget to every target that actually got the OPEN frame
|
|
386
|
-
//
|
|
387
|
-
//
|
|
388
|
-
//
|
|
389
|
-
//
|
|
390
|
-
//
|
|
391
|
-
//
|
|
392
|
-
//
|
|
385
|
+
// Completion breadcrumb (card dismiss) — fire-and-forget to every target that actually got the OPEN frame,
|
|
386
|
+
// so a slow/hung emit never delays returning to core AND every connection that saw the card learns it
|
|
387
|
+
// resolved elsewhere([1559]四「其余连接收 resolved/dismissed 通知」的直接落点——单连接场景退化为
|
|
388
|
+
// 原来的一次 emit)。**per-ctx 链在该 ctx 自己的 openP 之后且看它的布尔结果**([2557] 批3 F2 D1 修,
|
|
389
|
+
// 2026-08-06):emitOne 失败是 resolve(false) 不是 reject,旧形 `.then(() => emit(complete))` 对
|
|
390
|
+
// resolve 值全放行 ⇒ open 没送达的连接(瞬断型 emit 目标)也收 `tool_approval_complete` = 孤儿
|
|
391
|
+
// close,注释与代码曾不符(红先=wire-pairing-approval D1,修后翻正)。`delivered` 门后:
|
|
392
|
+
// `.then((delivered) => delivered ? c.emit(...) : undefined)` —— complete emit 若同步 throw,
|
|
393
|
+
// `.then` 回调内抛出转成 rejection 由尾部 `.catch` 吸收(链形不变)。
|
|
393
394
|
for (const c of aliveCtxs) {
|
|
394
395
|
void openByCtx
|
|
395
396
|
.get(c)
|
|
396
|
-
.then(() => c.emit({ type: "tool_approval_complete", approvalId: id, outcome: lastOutcome }))
|
|
397
|
+
.then((delivered) => (delivered ? c.emit({ type: "tool_approval_complete", approvalId: id, outcome: lastOutcome }) : undefined))
|
|
397
398
|
.catch(() => undefined);
|
|
398
399
|
}
|
|
399
400
|
// emit 全灭 = 卡从未送达任何人(≠人拒绝/TTL 走人)——同属「无活人可达」类 ⇒ "unavailable" 交 durable
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.6.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.11.0",
|
|
58
58
|
"@sema-agent/registry-core": "^0.14.0",
|
|
59
59
|
"e2b": "^2.28.0",
|
|
60
60
|
"libsodium-wrappers": "^0.8.4",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"sharp": "^0.35.3"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
|
-
"@sema-agent/sdk": "^6.
|
|
71
|
+
"@sema-agent/sdk": "^6.4.0",
|
|
72
72
|
"@types/libsodium-wrappers": "^0.7.14",
|
|
73
73
|
"@types/node": "22.10.2",
|
|
74
74
|
"@types/pg": "^8.20.0",
|