@sema-agent/server 7.83.0 → 7.84.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 +9 -5
- package/dist/capabilities/memory-notice.d.ts +6 -4
- package/dist/capabilities/memory-notice.js +1 -1
- package/dist/http/admission.js +7 -3
- package/dist/http/wire-types.d.ts +22 -0
- package/dist/memory-scope.d.ts +33 -5
- package/dist/memory-scope.js +10 -4
- package/dist/observability/run-terminal-log.d.ts +2 -0
- package/dist/observability/run-terminal-log.js +110 -1
- package/dist/request-key-closure.d.ts +7 -0
- package/dist/request-key-closure.js +1 -1
- package/dist/request-value-closure.d.ts +31 -0
- package/dist/request-value-closure.js +18 -0
- package/package.json +1 -1
|
@@ -25,6 +25,7 @@ import { gateExecutionLane } from "../runtime-caps-resolver.js";
|
|
|
25
25
|
import { applyRuntimeGovernance, stripDelegationTools } from "../runtime-governance.js";
|
|
26
26
|
import { HttpError, encodeCheckpointScope } from "../security.js";
|
|
27
27
|
import { memorySpecForRequest } from "../memory-scope.js";
|
|
28
|
+
import { singleMemberOffFieldIssue } from "../request-value-closure.js";
|
|
28
29
|
import { a2aForScenario, mcpForScenario } from "../config-center/facade.js";
|
|
29
30
|
import { normalizeAttachments, normalizeResilience, normalizeResumeAtMode, normalizeSuggestNextPrompts, promptProfileFromBody, resolveTaskLimits, retainBackgroundProcessesFromBody, taskAgentsSpecFragment, toolMaterializeStrategyFromBody, toolNameListFromBody } from "../spec-fields.js";
|
|
30
31
|
import { cwdHonored, deviceCwdHonored, effectiveHostWorkspace, inProcessSingleUserLane, isValidCwd, parseAdditionalDirectories, satisfiedByProcessCwd, shellEnvMismatchCount } from "../task-cwd.js";
|
|
@@ -36,6 +37,7 @@ import { redactSecrets } from "../trace/redact.js";
|
|
|
36
37
|
import { DeferredSandboxPathEnv, isSandboxPathAdjudicationLane, sandboxPathEnvSlots } from "./deferred-sandbox-path-env.js";
|
|
37
38
|
import { executionLaneHomeDir } from "../execution-lane-caps.js";
|
|
38
39
|
import { effectiveMemoryPersistenceCapable } from "./memory-boundary.js";
|
|
40
|
+
import { memoryFaceMounted } from "../memory-scope.js";
|
|
39
41
|
export function createResolveSpec(ctx) {
|
|
40
42
|
const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, getCenterPrompts, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, fenceSessionOwner, hookLlm, hookAgent, fleetBus, hookWakeBus, noteSessionShellGate, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, durableEnabled, approvalExemptionStore, singleUserAutoAcceptBaseline, checkpointStore, deploymentHooks, imageIndex, perTaskImage, sessionEnvSelection, liveQuestionFace, approverSeat, lockedKeys, } = ctx;
|
|
41
43
|
assertGuardPatternsUsable(config);
|
|
@@ -59,9 +61,9 @@ export function createResolveSpec(ctx) {
|
|
|
59
61
|
throw new HttpError(400, `cwd "${conflict.cwd.asSpelled}" is inside this session's scratchpad "${conflict.scratchpadDir.asSpelled}" — the scratchpad is the fs-write gate's EXEMPT directory, so a task rooted there would auto-approve every write under its own root and the engine refuses to build the gate at all (config.write_exempt_dir_covers_root). Point cwd at the project directory instead (or send no cwd and let the session keep the one it has): this session registers the cwd you send, so a task that happens not to mount the write gate would still leave every LATER task on this session rooted in the exemption. A workflow that really is meant to write without prompts says so with a knob that means it (a permission mode, an open write face) and still keeps its root outside the scratchpad — those knobs do not lift this refusal, because the conflict is between two fields of this request, not a permission level.`, { code: "request.field_conflict", extra: { cwd: conflict.cwd, scratchpadDir: conflict.scratchpadDir } });
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
|
-
const
|
|
63
|
-
if (
|
|
64
|
-
throw new HttpError(400,
|
|
64
|
+
const offFieldIssue = singleMemberOffFieldIssue(body);
|
|
65
|
+
if (offFieldIssue) {
|
|
66
|
+
throw new HttpError(400, offFieldIssue.message, { code: "request.field_invalid" });
|
|
65
67
|
}
|
|
66
68
|
assertRequestA2aUnlocked(body.a2aPeers, lockedKeys);
|
|
67
69
|
const gated = await gateScenarioAndAppend(body, auth, opts);
|
|
@@ -373,7 +375,9 @@ export function createResolveSpec(ctx) {
|
|
|
373
375
|
return { ...(Object.keys(merged).length > 0 ? { limits: merged } : {}), ...(declarations.length > 0 ? { configOverrides: declarations } : {}) };
|
|
374
376
|
})(),
|
|
375
377
|
...(config.requirePrincipal !== true ? { backgroundScope: "session" } : {}),
|
|
376
|
-
memory: memoryEngine
|
|
378
|
+
memory: memoryEngine
|
|
379
|
+
? memorySpecForRequest(auth?.memoryScope, body.memoryWrite, s4DefaultScopes, { multiTenant: config.requirePrincipal === true }, { memory: body.memory, memoryCapture: body.memoryCapture })
|
|
380
|
+
: undefined,
|
|
377
381
|
...((v) => (v !== undefined ? { memoryPersistenceCapable: v } : {}))(effectiveMemoryPersistenceCapable(config)),
|
|
378
382
|
tools: ((base) => {
|
|
379
383
|
const extra = [...(selectEnvTool ? [selectEnvTool] : []), ...(sendUserFileToolSpec ? [sendUserFileToolSpec] : [])];
|
|
@@ -533,7 +537,7 @@ export function createResolveSpec(ctx) {
|
|
|
533
537
|
governed = { ...governed, tools: stripDelegationTools(governed.tools) };
|
|
534
538
|
}
|
|
535
539
|
if (shouldDiscloseNoPersistentMemory({
|
|
536
|
-
|
|
540
|
+
memoryFaceMounted: memoryFaceMounted(governed.memory),
|
|
537
541
|
declaredCapable: effectiveMemoryPersistenceCapable(config),
|
|
538
542
|
hands: gated.hands,
|
|
539
543
|
handsReadOnly: governed.handsReadOnly === true,
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* roster 把落盘路**整批**卸掉时,手带在场也等于没有写通道),但负控仍然成立:必须**全部**已知落盘路
|
|
19
19
|
* 都不在才算断链 —— `excludeTools:["Write"]` 不算(Bash/Edit 还在),请求面摘一个工具名改不动结论。
|
|
20
20
|
*/
|
|
21
|
-
import { type PromptProvider
|
|
21
|
+
import { type PromptProvider } from "@sema-agent/core";
|
|
22
22
|
import type { ScenarioHands } from "./hands-lane.js";
|
|
23
23
|
/**
|
|
24
24
|
* 「已知落盘路」的工具名闭集 —— 与 core 的 `rosterCanPersist` 同口径:**文件写工具**(Write/Edit/
|
|
@@ -35,9 +35,11 @@ export declare const KNOWN_PERSISTENCE_TOOL_NAMES: readonly ["Write", "Edit", "N
|
|
|
35
35
|
export declare const NO_PERSISTENT_MEMORY_SECTION_ID = "sema/memory.no-persistent";
|
|
36
36
|
/** 判别式的全部输入 —— 每一位都是**部署/装配期事实**,没有一位来自请求体的自我表态。 */
|
|
37
37
|
export interface MemoryFaceFacts {
|
|
38
|
-
/**
|
|
39
|
-
* remote lane
|
|
40
|
-
|
|
38
|
+
/** 这一腿挂不挂记忆面 —— 由唯一属主 `memory-scope.ts` `memoryFaceMounted(spec.memory)` 算出(与 core `prepareMemory`
|
|
39
|
+
* 同判:`normalizeMemorySpec(...)?.enabled === true`)。`false` = 引擎未接 / 多租户 dark / remote lane fail-closed /
|
|
40
|
+
* S-405 `memory:"off"`(键在场但 `enabled:false`)—— 殊途同归:core 不会 prepare 记忆,也就不会挂任何披露。
|
|
41
|
+
* 🔴 不再收 `TaskSpec.memory` 本体:按「键在不在场」判是 7.84.0 前的病(合并复审 车HF F1)。 */
|
|
42
|
+
memoryFaceMounted: boolean;
|
|
41
43
|
/** 部署声明 `MEMORY_PERSISTENCE_CAPABLE`(三态)。`true` = operator 自证有推断看不见的持久通道
|
|
42
44
|
* (自定义 writer / MCP 记忆服务)⇒ 披露会变成假话,撤回;`false` = 强制披露;缺席 ⇒ 走推断。 */
|
|
43
45
|
declaredCapable: boolean | undefined;
|
|
@@ -9,7 +9,7 @@ function anyPersistenceToolSurvives(excludeTools) {
|
|
|
9
9
|
return KNOWN_PERSISTENCE_TOOL_NAMES.some((name) => !excluded.has(name));
|
|
10
10
|
}
|
|
11
11
|
export function shouldDiscloseNoPersistentMemory(facts) {
|
|
12
|
-
if (facts.
|
|
12
|
+
if (facts.memoryFaceMounted)
|
|
13
13
|
return false;
|
|
14
14
|
if (facts.declaredCapable !== undefined)
|
|
15
15
|
return !facts.declaredCapable;
|
package/dist/http/admission.js
CHANGED
|
@@ -3,6 +3,7 @@ import { isModelAllowlisted, matchCatalogModel } from "../model-select.js";
|
|
|
3
3
|
import { HttpError } from "../security.js";
|
|
4
4
|
import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_PERMISSION_RULES, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS, parseTaskSettings, skillListingBudgetFractionOk, skillListingMaxDescOk, taskSettingsKeyIssue } from "../task-settings.js";
|
|
5
5
|
import { taskBodyKeyIssue } from "../request-key-closure.js";
|
|
6
|
+
import { singleMemberOffFieldIssue } from "../request-value-closure.js";
|
|
6
7
|
import { selfOrchestrationDenial } from "../task-workflow.js";
|
|
7
8
|
import { centerEntitlementSourceWired } from "../runtime-caps-resolver.js";
|
|
8
9
|
import { parseHooksConfig } from "../hooks/hook-runner.js";
|
|
@@ -307,9 +308,12 @@ export function createAdmission(ctx) {
|
|
|
307
308
|
sendError(res, 400, "request.field_invalid", "memoryWrite must be a boolean (false = pause memory writes for this run)");
|
|
308
309
|
return null;
|
|
309
310
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
311
|
+
{
|
|
312
|
+
const offFieldIssue = singleMemberOffFieldIssue(body);
|
|
313
|
+
if (offFieldIssue) {
|
|
314
|
+
sendError(res, 400, "request.field_invalid", offFieldIssue.message);
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
313
317
|
}
|
|
314
318
|
if (body.requireExistingSession !== undefined && typeof body.requireExistingSession !== "boolean") {
|
|
315
319
|
sendError(res, 400, "request.field_invalid", "requireExistingSession must be a boolean");
|
|
@@ -320,6 +320,28 @@ export interface TaskRequestBody {
|
|
|
320
320
|
* 记忆面暗的部署(引擎未接线 / 多租户)上本键随 `memory` 整体缺席而无处落 —— 那里本就零采集。
|
|
321
321
|
* Rides the persisted body onto resume legs(与 memoryWrite 同姿势)。 */
|
|
322
322
|
memoryCapture?: "off";
|
|
323
|
+
/** core [ref] r5 §5 W3(server S-405):**请求级「记忆整面关」声明** —— `"off"` = 这一次运行**不挂记忆面**
|
|
324
|
+
* (没有 `# Memory` 段/索引/三只记忆工具/写准入)。单成员闭集,**没有** `"on"`(缺席就是记忆照常挂的唯一形;
|
|
325
|
+
* 声明只能单向收紧)。透传为 `TaskSpec.memory.enabled:false` —— ⚠️ **保留事实,不折成整键缺席**:core 的
|
|
326
|
+
* 终局观测面把「有 spec 但被关」读作 `reason:"disabled"`、把「没有 spec」读作 `reason:"no-spec"`,审计上
|
|
327
|
+
* 不是一件事,所以本仓铸的是 `{ …正常 scopes, enabled:false }`。
|
|
328
|
+
* ⚠️ **语义如实**:这是「不挂记忆面」,**不是**文件系统级的禁读禁写 —— 记忆根若落在本次授权的工作区内,
|
|
329
|
+
* 普通 Read/Write 照走普通围栏(没有记忆面 = 没有记忆写门,不是多了一道禁令)。
|
|
330
|
+
* 与 `memoryCapture:"off"` **不是一个轴**:那是「记忆照常挂,但本会话的内容永远不进长期记忆」
|
|
331
|
+
* (采集面,落一条单向会话记录);这是「本次运行整个记忆面都不挂」(读写整面,逐请求)。
|
|
332
|
+
* 🔴 **两键同发时不要以为买到了两件事**(亲核 core 7.21.1 `prepare-memory.js:48/:55`):core 把 capture 腿与
|
|
333
|
+
* 引擎挂载合取,`enabled:false` 下 capture 的**单向会话记录不落**(core 发 `phase:"config"` 一行披露)⇒
|
|
334
|
+
* 下一 turn 不带本键时采集照常。要「这个会话永久别记」,那一 turn 就**别**同时发本键。详见契约 §12.7。
|
|
335
|
+
* 与 `memoryWrite:false`(本 run 只读平面:照常挂、照常读、harvest 不提交)同样正交、同样可并存。
|
|
336
|
+
* 🔴 **与 `GET /v1/capabilities` 的 legacy 能力位 `memory`(恒假)同名不同物**:那一位说的是退役的
|
|
337
|
+
* MemoryStore HTTP 动词族,与本键零关系 —— 别按那一位判断本键可不可用。
|
|
338
|
+
* ⚠️ 拼写门**两腿同判、响亮拒**:fresh 400 `request.field_invalid`;resume 重放同判(不做 resume 降级 ——
|
|
339
|
+
* 把一条收紧声明按打字错误静默丢掉是不可逆方向的静默降级)。两腿共用同一只判据
|
|
340
|
+
* (`request-value-closure.ts` 的 `singleMemberOffFieldIssue`,与 `memoryCapture` 同表)。
|
|
341
|
+
* 记忆面暗的部署(引擎未接线 / 多租户)上 `memory` 整键本就缺席,本声明无处落也无事可关。
|
|
342
|
+
* Rides the persisted body onto resume legs(与 `memoryWrite` 同姿势)。
|
|
343
|
+
* ⏳ 子代地板(`ParentSpecSeat.memoryOff`:父关 ⇒ 子代/孙代同关)候 core 7.22.0,**本版不做**。 */
|
|
344
|
+
memory?: "off";
|
|
323
345
|
/** 142-S4 projectId 线程化([ref] §2):这个 run 归属的项目(center 登记簿键,generic lowercase
|
|
324
346
|
* UUID — core S1 `resolveProjectId` marker 或 S3 无仓 mint 的产物;客户端只透传,零铸造权威)。是「哪个
|
|
325
347
|
* 项目」的选择器,不是身份/capability — scope 的 tenant 段永远来自 verified principal。SHAPE 门在
|
package/dist/memory-scope.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FileMemoryEngineBackend } from "@sema-agent/core";
|
|
1
|
+
import { FileMemoryEngineBackend, type TaskSpec } from "@sema-agent/core";
|
|
2
2
|
import type { ServiceConfig } from "./config-types.js";
|
|
3
3
|
/** 前缀词表本体(供门/诊断读;判定一律走 {@link isV2ScopeKey},别在别处重写 startsWith 链)。 */
|
|
4
4
|
export declare const V2_SCOPE_PREFIXES: readonly string[];
|
|
@@ -175,6 +175,10 @@ onIncident?: (err: Error & {
|
|
|
175
175
|
* commits nothing). Absent/true ⇒ normal read+write. No scope (engine off / multi-tenant dark) ⇒ undefined
|
|
176
176
|
* (memory feature off for this run). Pure (testable in isolation) — main.ts's resolveSpec composes it with the
|
|
177
177
|
* engine-backend presence guard.
|
|
178
|
+
*
|
|
179
|
+
* 🔴 **三轴正交**(S-405 起;三者可任意并存,互不吞):`memoryWrite:false` = 本 run 只读平面(挂、读、
|
|
180
|
+
* harvest 不提交)· `memoryCapture:"off"` = 采集面关(挂、读、本会话内容永不进长期记忆)· `memory:"off"` =
|
|
181
|
+
* **整面关**(`enabled:false`,这一次连记忆面都不挂)。后两者从 `declarations` 进(键名与 wire 逐字同名)。
|
|
178
182
|
*/
|
|
179
183
|
export declare function memorySpecForRequest(scope: string | undefined, memoryWrite: boolean | undefined, defaultScopes?: string[],
|
|
180
184
|
/** [ref] 件A([ref] 件3④):origin 盖章的部署形态维(N2)。`multiTenant=true`(requirePrincipal)
|
|
@@ -185,14 +189,38 @@ export declare function memorySpecForRequest(scope: string | undefined, memoryWr
|
|
|
185
189
|
originPolicy?: {
|
|
186
190
|
multiTenant: boolean;
|
|
187
191
|
},
|
|
188
|
-
/**
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
memoryCapture
|
|
192
|
+
/** 请求体上的**单成员闭集声明**(键名与 wire 逐字同名,零翻译;拼写门的唯一属主是
|
|
193
|
+
* `request-value-closure.ts`,两腿共用 —— 到这里的值已经过门,只可能是 `"off"` 或缺席)。
|
|
194
|
+
*
|
|
195
|
+
* · `memoryCapture:"off"`([ref] §2.1,[ref])⇒ `TaskSpec.memory.capture:"off"`:**采集面**关 ——
|
|
196
|
+
* 记忆照常挂、照常读,但本会话的内容永远不进长期记忆(core 落一条单向会话记录,按部署姿态 + verdict 裁)。
|
|
197
|
+
* · `memory:"off"`(core [ref] r5 §5 W3,S-405)⇒ `TaskSpec.memory.enabled:false`:**整面**关 ——
|
|
198
|
+
* 这一次运行不挂记忆面。**保留事实不折缺席**(codex r2 #4):core 的终局观测面把「有 spec 但被关」读作
|
|
199
|
+
* `reason:"disabled"`、把「整键缺席」读作 `reason:"no-spec"`,审计上不是一件事;而 `normalizeMemorySpec`
|
|
200
|
+
* 在 scopes 为空时整体返回 `undefined`,所以**关的那一形也必须带正常 scopes**。
|
|
201
|
+
*
|
|
202
|
+
* 两键与 `memoryWrite`(本 run 只读平面)在**铸型上**三轴正交、可并存,本函数不替谁做取舍;
|
|
203
|
+
* 整键缺席 ⇒ 都不铸(形状逐字节不变=additive 锁)。
|
|
204
|
+
* ⚠️ **铸型正交 ≠ 裁决正交**(亲核 core 7.21.1 `dist/core/runner/prepare-memory.js:48/:55`):core 把
|
|
205
|
+
* capture 腿与引擎挂载**合取**(`captureDeclared = captureDeclaredRaw && useMemoryEngine`),所以
|
|
206
|
+
* `memory:"off"` 那一 turn 上 capture 的**单向会话记录不落**(core 发 `phase:"config"` 一行响亮披露)。
|
|
207
|
+
* 两键同发要不要一起送、送了买到什么,口径全文在契约 §12.7;钉在 memory-off-request.test.ts ⑤。 */
|
|
208
|
+
declarations?: {
|
|
209
|
+
memory?: "off";
|
|
210
|
+
memoryCapture?: "off";
|
|
211
|
+
}): {
|
|
192
212
|
scopes: string[];
|
|
193
213
|
writeScope?: string | null;
|
|
194
214
|
scopeContract?: "v2";
|
|
195
215
|
scopeOrigins?: Record<string, "deployment" | "request">;
|
|
196
216
|
capture?: "off";
|
|
217
|
+
enabled?: false;
|
|
197
218
|
} | undefined;
|
|
219
|
+
/**
|
|
220
|
+
* 「这一腿挂不挂记忆面」的**唯一属主**(合并复审 车HF F1,2026-09-18):与 core `prepareMemory` 的判据同源 ——
|
|
221
|
+
* `normalizeMemorySpec(spec)?.enabled === true`。S-405 之后 `memory:"off"` 铸的是「键在场 + `enabled:false`」(保留事实),
|
|
222
|
+
* 任何按「键在不在场」判挂没挂的读者从那一天起都读错(改前 `memory-notice` 正是那样,把无记忆面的披露一起吞掉)。
|
|
223
|
+
* 消费方拿本函数的布尔,不拿 `spec.memory` 自己判。
|
|
224
|
+
*/
|
|
225
|
+
export declare function memoryFaceMounted(memory: TaskSpec["memory"]): boolean;
|
|
198
226
|
//# sourceMappingURL=memory-scope.d.ts.map
|
package/dist/memory-scope.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
-
import { FileMemoryEngineBackend, resolveMemoryEngineRoot, deriveControlPlaneDir, formatUserScope, formatProjScope } from "@sema-agent/core";
|
|
2
|
+
import { FileMemoryEngineBackend, resolveMemoryEngineRoot, deriveControlPlaneDir, formatUserScope, formatProjScope, normalizeMemorySpec } from "@sema-agent/core";
|
|
3
3
|
import { assertMemoryScopeWidth } from "./plugins/memory-key-guards.js";
|
|
4
4
|
const V2_SCOPE_PREFIX_BY_KIND = {
|
|
5
5
|
user: "user:",
|
|
@@ -96,7 +96,7 @@ export function memoryEngineBackendFor(config, fallbackRoot, onIncident) {
|
|
|
96
96
|
root: plane.root,
|
|
97
97
|
};
|
|
98
98
|
}
|
|
99
|
-
export function memorySpecForRequest(scope, memoryWrite, defaultScopes, originPolicy,
|
|
99
|
+
export function memorySpecForRequest(scope, memoryWrite, defaultScopes, originPolicy, declarations) {
|
|
100
100
|
if (!scope)
|
|
101
101
|
return undefined;
|
|
102
102
|
const isV2 = isV2ScopeKey;
|
|
@@ -113,7 +113,13 @@ export function memorySpecForRequest(scope, memoryWrite, defaultScopes, originPo
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
const origins = Object.keys(stamped).length > 0 ? { scopeOrigins: stamped } : {};
|
|
116
|
-
const capture = memoryCapture === "off" ? { capture: "off" } : {};
|
|
117
|
-
|
|
116
|
+
const capture = declarations?.memoryCapture === "off" ? { capture: "off" } : {};
|
|
117
|
+
const enabled = declarations?.memory === "off" ? { enabled: false } : {};
|
|
118
|
+
return memoryWrite === false
|
|
119
|
+
? { ...layered, writeScope: null, ...v2, ...origins, ...capture, ...enabled }
|
|
120
|
+
: { ...layered, ...v2, ...origins, ...capture, ...enabled };
|
|
121
|
+
}
|
|
122
|
+
export function memoryFaceMounted(memory) {
|
|
123
|
+
return normalizeMemorySpec(memory)?.enabled === true;
|
|
118
124
|
}
|
|
119
125
|
//# sourceMappingURL=memory-scope.js.map
|
|
@@ -102,6 +102,8 @@ export declare function redactTerminalResult<T extends {
|
|
|
102
102
|
* 声明成 `{path,edits}[]` 会逼每一个喂坏形的调用点铸一次宽松断言 —— 而「坏形从哪来」正是本座要
|
|
103
103
|
* 处理的事(同一份不信任在 `remoteEnvFailures` 的实现里也已成文)。运行期逐项判形,不抛。 */
|
|
104
104
|
editedFiles?: unknown;
|
|
105
|
+
/** S-394:形上同样 `unknown` —— 本函数是读盘面,`stats` 的树形逐层现判(见 {@link redactStats})。 */
|
|
106
|
+
stats?: unknown;
|
|
105
107
|
} | null | undefined>(result: T): T;
|
|
106
108
|
/** S-107(第五轮合并重扫 wf_f00c00bc 两根 [high]):同一个 `TaskResult` 除 run 行外还被逐字追加进 run_events 的 `done` 行
|
|
107
109
|
* (bg/verify/cascade/stream/resume 五腿),`failed` 行带原文 `errorMessage`,`GET /v1/runs/:id/events` 原样回放。
|
|
@@ -104,7 +104,7 @@ const RESULT_KEY_TREATMENT = {
|
|
|
104
104
|
effectiveWriteFace: "verbatim",
|
|
105
105
|
effectiveReadDenyPatterns: "verbatim",
|
|
106
106
|
effectiveMemoryScopes: "verbatim",
|
|
107
|
-
stats: "
|
|
107
|
+
stats: "redacted",
|
|
108
108
|
};
|
|
109
109
|
const LEGACY_RESULT_LEAVES = ["errorMessage", "blockedReason", "checkpointGate"];
|
|
110
110
|
export const REDACTABLE_RESULT_LEAVES = [
|
|
@@ -207,6 +207,13 @@ export function redactTerminalResult(result) {
|
|
|
207
207
|
: r.rewindNotes;
|
|
208
208
|
changed = true;
|
|
209
209
|
}
|
|
210
|
+
if (r.stats !== undefined) {
|
|
211
|
+
const next = redactStats(r.stats);
|
|
212
|
+
if (next !== r.stats) {
|
|
213
|
+
out.stats = next;
|
|
214
|
+
changed = true;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
210
217
|
return changed ? out : result;
|
|
211
218
|
}
|
|
212
219
|
export function redactLedgerEventData(type, data) {
|
|
@@ -317,6 +324,108 @@ export function redactRiskDescriptor(rd) {
|
|
|
317
324
|
}
|
|
318
325
|
return changed ? out : rd;
|
|
319
326
|
}
|
|
327
|
+
const STATS_KEY_TREATMENT = {
|
|
328
|
+
turns: "verbatim",
|
|
329
|
+
tokens: "verbatim",
|
|
330
|
+
toolCalls: "verbatim",
|
|
331
|
+
usageMissing: "verbatim",
|
|
332
|
+
promptTokens: "verbatim",
|
|
333
|
+
cachedTokens: "verbatim",
|
|
334
|
+
cacheWriteTokens: "verbatim",
|
|
335
|
+
cacheHitRate: "verbatim",
|
|
336
|
+
totalInputTokens: "verbatim",
|
|
337
|
+
outputTokens: "verbatim",
|
|
338
|
+
cacheWriteTokensLong: "verbatim",
|
|
339
|
+
costMicroUsd: "verbatim",
|
|
340
|
+
nested: "verbatim",
|
|
341
|
+
memory: "verbatim",
|
|
342
|
+
suggestions: "verbatim",
|
|
343
|
+
costBreakdown: "verbatim",
|
|
344
|
+
mechanisms: "mechanisms",
|
|
345
|
+
humanReview: "human-review",
|
|
346
|
+
};
|
|
347
|
+
const STATS_MECHANISMS_TREATMENT = {
|
|
348
|
+
finalVerifyInjected: "verbatim",
|
|
349
|
+
finalVerifyInjections: "verbatim",
|
|
350
|
+
attachmentsInjected: "verbatim",
|
|
351
|
+
approachNoticesSent: "verbatim",
|
|
352
|
+
repetitionCuts: "verbatim",
|
|
353
|
+
repetitionSpared: "verbatim",
|
|
354
|
+
repetitionEvents: "repetition-rows",
|
|
355
|
+
reminderDisclosures: "verbatim",
|
|
356
|
+
};
|
|
357
|
+
const STATS_HUMAN_REVIEW_TREATMENT = {
|
|
358
|
+
count: "verbatim",
|
|
359
|
+
totalWaitMs: "verbatim",
|
|
360
|
+
gates: "gate-rows",
|
|
361
|
+
};
|
|
362
|
+
const STATS_GATE_ROW_TREATMENT = {
|
|
363
|
+
kind: "verbatim",
|
|
364
|
+
waitMs: "verbatim",
|
|
365
|
+
decision: "verbatim",
|
|
366
|
+
toolName: "verbatim",
|
|
367
|
+
toolArg: "model-text",
|
|
368
|
+
};
|
|
369
|
+
const STATS_REPETITION_EVENT_TREATMENT = {
|
|
370
|
+
turn: "verbatim",
|
|
371
|
+
action: "verbatim",
|
|
372
|
+
rule: "verbatim",
|
|
373
|
+
period: "verbatim",
|
|
374
|
+
reps: "verbatim",
|
|
375
|
+
segment: "model-text",
|
|
376
|
+
};
|
|
377
|
+
function redactStatsNode(node, table) {
|
|
378
|
+
if (node === null || typeof node !== "object" || Array.isArray(node))
|
|
379
|
+
return node;
|
|
380
|
+
const n = node;
|
|
381
|
+
let changed = false;
|
|
382
|
+
const out = { ...n };
|
|
383
|
+
const into = (v, t) => redactStatsNode(v, t);
|
|
384
|
+
const rows = (v, t) => {
|
|
385
|
+
if (!Array.isArray(v))
|
|
386
|
+
return v;
|
|
387
|
+
const next = v.map((row) => redactStatsNode(row, t));
|
|
388
|
+
return next.every((x, i) => x === v[i]) ? v : next;
|
|
389
|
+
};
|
|
390
|
+
for (const [key, treatment] of Object.entries(table)) {
|
|
391
|
+
const v = n[key];
|
|
392
|
+
if (v === undefined)
|
|
393
|
+
continue;
|
|
394
|
+
let next = v;
|
|
395
|
+
switch (treatment) {
|
|
396
|
+
case "verbatim":
|
|
397
|
+
break;
|
|
398
|
+
case "model-text":
|
|
399
|
+
if (typeof v === "string")
|
|
400
|
+
next = redactModelText(v);
|
|
401
|
+
break;
|
|
402
|
+
case "mechanisms":
|
|
403
|
+
next = into(v, STATS_MECHANISMS_TREATMENT);
|
|
404
|
+
break;
|
|
405
|
+
case "human-review":
|
|
406
|
+
next = into(v, STATS_HUMAN_REVIEW_TREATMENT);
|
|
407
|
+
break;
|
|
408
|
+
case "gate-rows":
|
|
409
|
+
next = rows(v, STATS_GATE_ROW_TREATMENT);
|
|
410
|
+
break;
|
|
411
|
+
case "repetition-rows":
|
|
412
|
+
next = rows(v, STATS_REPETITION_EVENT_TREATMENT);
|
|
413
|
+
break;
|
|
414
|
+
default: {
|
|
415
|
+
const unhandled = treatment;
|
|
416
|
+
throw new Error(`unhandled stats treatment: ${String(unhandled)}`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (next !== v) {
|
|
420
|
+
out[key] = next;
|
|
421
|
+
changed = true;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return changed ? out : node;
|
|
425
|
+
}
|
|
426
|
+
function redactStats(stats) {
|
|
427
|
+
return redactStatsNode(stats, STATS_KEY_TREATMENT);
|
|
428
|
+
}
|
|
320
429
|
const CHECKPOINT_GATE_TREATMENT = {
|
|
321
430
|
kind: "verbatim",
|
|
322
431
|
reason: "text",
|
|
@@ -19,6 +19,13 @@ export declare function scanKeyClosure(bag: object, prefix: string, accepted: Re
|
|
|
19
19
|
}): void;
|
|
20
20
|
/** 一张分类表的文案片段(封顶后只报数)。 */
|
|
21
21
|
export declare function keyClosureClause(names: string[], label: string): string;
|
|
22
|
+
/** 剥掉索引签名,只留**具名**键 —— `TaskRequestBody` 上那条 `[k: string]: unknown` 会让
|
|
23
|
+
* `keyof` 退化成 `string`,双向门就永远恒真(零判别力)。
|
|
24
|
+
* 🔴 **导出**(S-405):姊妹门 `request-value-closure.ts` 的双向钉要同一个剥法 —— 抄第二份 = 两处哪天
|
|
25
|
+
* 分岔(一边看得见索引签名、一边看不见)谁都发现不了,而两边的判别力都建立在「剥干净了」这句话上。 */
|
|
26
|
+
export type NamedKeysOf<T> = {
|
|
27
|
+
[K in keyof T as string extends K ? never : number extends K ? never : K]: T[K];
|
|
28
|
+
};
|
|
22
29
|
/**
|
|
23
30
|
* FRESH 提交面的**顶层**键闭集裁决:受理集之外的键 ⇒ 返回拒体(调用方 400 `request.body_shape`);
|
|
24
31
|
* 全部受理 ⇒ `null`。
|
|
@@ -22,7 +22,7 @@ const TASK_BODY_DECLARED_KEYS = [
|
|
|
22
22
|
"appendSystemPrompt", "attachmentIds", "attachments", "capabilitiesNeeded", "cascade", "clientContext",
|
|
23
23
|
"compaction", "compactionModel", "council", "cwd", "debate", "deferTools", "enableFork", "excludeTools",
|
|
24
24
|
"finalVerification", "forwardSubagentEvents", "images", "interactiveTools", "jobId", "limits", "maxCostUsd",
|
|
25
|
-
"maxTokens", "mcpServers", "memoryCapture", "memoryWrite", "model", "objective", "oneShot", "outputRetries",
|
|
25
|
+
"maxTokens", "mcpServers", "memory", "memoryCapture", "memoryWrite", "model", "objective", "oneShot", "outputRetries",
|
|
26
26
|
"outputSchema", "permissionMode", "projectId", "promptProfile", "reasoningEffort", "requireExistingSession",
|
|
27
27
|
"resilience", "restoreFiles", "resumeAt", "resumeAtMode", "retainBackgroundProcesses", "retainSubagentSessions",
|
|
28
28
|
"rewindFiles", "rewindFilesTo", "sandboxImageProfile", "scenario", "scratchpadDir", "selfOrchestration",
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** [ref] §2.1([ref]):`memoryCapture` 的拼写门文案。**wire 可见文本** —— 改一个字就是对端契约漂移
|
|
2
|
+
* (SDK/壳/文档锚它),字节冻结在 `test/api-error-text-freeze.test.ts` 的常量锚格。 */
|
|
3
|
+
export declare const MEMORY_CAPTURE_SPELLING_MESSAGE = "memoryCapture must be the string 'off' when present (the session-level memory-capture opt-out declaration; absent = capture as usual \u2014 there is no 'on' spelling)";
|
|
4
|
+
/** core [ref] r5 §5 W3(S-405):请求级「记忆整面关」`memory` 的拼写门文案。同上,wire 可见、字节冻结。
|
|
5
|
+
* 文案刻意点明 capture 的分工 —— 两个键都在记忆轴上、形又一模一样,拿错的人要在同一条回执里读到该用哪个。 */
|
|
6
|
+
export declare const MEMORY_OFF_SPELLING_MESSAGE = "memory must be the string 'off' when present (the request-level declaration that this run mounts no memory face at all; absent = memory as usual \u2014 there is no 'on' spelling; to keep memory mounted but stop this session's content from entering long-term memory, send memoryCapture: 'off' instead)";
|
|
7
|
+
/**
|
|
8
|
+
* **单成员闭集键表** —— 合法值只有 `"off"`,缺席=照常,没有 `"on"`。
|
|
9
|
+
*
|
|
10
|
+
* 🔴 顺序 = **裁决顺序**,且是**既存键在前**:一个两键都畸形的存量体,判决与加 `memory` 之前逐字节相同
|
|
11
|
+
* (新键不许改写既有体的回执)。新增同形键一律**追加在尾部**。
|
|
12
|
+
*/
|
|
13
|
+
declare const SINGLE_MEMBER_OFF_FIELDS: readonly [{
|
|
14
|
+
readonly field: "memoryCapture";
|
|
15
|
+
readonly message: "memoryCapture must be the string 'off' when present (the session-level memory-capture opt-out declaration; absent = capture as usual — there is no 'on' spelling)";
|
|
16
|
+
}, {
|
|
17
|
+
readonly field: "memory";
|
|
18
|
+
readonly message: "memory must be the string 'off' when present (the request-level declaration that this run mounts no memory face at all; absent = memory as usual — there is no 'on' spelling; to keep memory mounted but stop this session's content from entering long-term memory, send memoryCapture: 'off' instead)";
|
|
19
|
+
}];
|
|
20
|
+
/** 一次值闭集裁决的产物:出问题的键名(机读)+ 一句 wire 文案。 */
|
|
21
|
+
export interface SingleMemberOffFieldIssue {
|
|
22
|
+
field: (typeof SINGLE_MEMBER_OFF_FIELDS)[number]["field"];
|
|
23
|
+
message: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 单成员闭集键的值形裁决:表内任一键在场且不是字面 `"off"` ⇒ 返回拒体(调用方 400 `request.field_invalid`);
|
|
27
|
+
* 全部合法或全部缺席 ⇒ `null`。**两腿(fresh admission / resume 重放)共用这一只**。
|
|
28
|
+
*/
|
|
29
|
+
export declare function singleMemberOffFieldIssue(raw: unknown): SingleMemberOffFieldIssue | null;
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=request-value-closure.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const MEMORY_CAPTURE_SPELLING_MESSAGE = "memoryCapture must be the string 'off' when present (the session-level memory-capture opt-out declaration; absent = capture as usual — there is no 'on' spelling)";
|
|
2
|
+
export const MEMORY_OFF_SPELLING_MESSAGE = "memory must be the string 'off' when present (the request-level declaration that this run mounts no memory face at all; absent = memory as usual — there is no 'on' spelling; to keep memory mounted but stop this session's content from entering long-term memory, send memoryCapture: 'off' instead)";
|
|
3
|
+
const SINGLE_MEMBER_OFF_FIELDS = [
|
|
4
|
+
{ field: "memoryCapture", message: MEMORY_CAPTURE_SPELLING_MESSAGE },
|
|
5
|
+
{ field: "memory", message: MEMORY_OFF_SPELLING_MESSAGE },
|
|
6
|
+
];
|
|
7
|
+
export function singleMemberOffFieldIssue(raw) {
|
|
8
|
+
if (raw === null || raw === undefined || typeof raw !== "object" || Array.isArray(raw))
|
|
9
|
+
return null;
|
|
10
|
+
const body = raw;
|
|
11
|
+
for (const { field, message } of SINGLE_MEMBER_OFF_FIELDS) {
|
|
12
|
+
const value = body[field];
|
|
13
|
+
if (value !== undefined && value !== "off")
|
|
14
|
+
return { field, message };
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=request-value-closure.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/server",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.84.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",
|