@sema-agent/server 7.69.0 → 7.71.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/README.md +4 -1
- package/README.zh-CN.md +4 -1
- package/USAGE.md +109 -10
- package/dist/approval-ask-machine.d.ts +1 -1
- package/dist/approval-card.d.ts +3 -2
- package/dist/approval-content-kind.d.ts +13 -7
- package/dist/approval-content-kind.js +3 -1
- package/dist/approval-reconciler.d.ts +0 -54
- package/dist/approval-reconciler.js +2 -2
- package/dist/approval.js +3 -2
- package/dist/bench/s1/live-deps.js +2 -0
- package/dist/boot/execution-env.js +12 -2
- package/dist/boot/memory-consolidation.d.ts +2 -1
- package/dist/boot/parked-revive-gate.d.ts +2 -0
- package/dist/boot/parked-revive-gate.js +15 -10
- package/dist/boot/resolve-spec.js +8 -3
- package/dist/boot/runner-deps.d.ts +50 -46
- package/dist/boot/runner-deps.js +12 -10
- package/dist/brain.d.ts +3 -1
- package/dist/brain.js +2 -1
- package/dist/budget.js +3 -2
- package/dist/config-catalog.d.ts +31 -2
- package/dist/config-catalog.js +44 -24
- package/dist/config-types.d.ts +31 -1
- package/dist/config-types.js +1 -0
- package/dist/config.js +15 -3
- package/dist/deployment-governance.d.ts +6 -3
- package/dist/deployment-governance.js +2 -1
- package/dist/execution-lane-caps.d.ts +51 -37
- package/dist/execution-lane-caps.js +23 -0
- package/dist/fleet/fleet-bus.d.ts +22 -1
- package/dist/fleet/fleet-bus.js +2 -1
- package/dist/http/routes/approvals-assistant.js +3 -12
- package/dist/http/routes/diagnostics.js +4 -0
- package/dist/http/routes/runs.js +2 -3
- package/dist/http/routes/tasks.js +22 -18
- package/dist/http/server.js +21 -7
- package/dist/index.d.ts +1 -1
- package/dist/leader/wire.js +4 -3
- package/dist/main.js +6 -4
- package/dist/memory-operator-faces.d.ts +1 -1
- package/dist/memory-scope.d.ts +24 -0
- package/dist/memory-scope.js +12 -3
- package/dist/observability/metrics.d.ts +4 -6
- package/dist/observability/metrics.js +5 -1
- package/dist/observability/permission-rule-events.d.ts +20 -0
- package/dist/observability/permission-rule-events.js +28 -0
- package/dist/observability/run-terminal-log.d.ts +13 -22
- package/dist/observability/run-terminal-log.js +187 -47
- package/dist/observability/tool-trace.d.ts +12 -0
- package/dist/observability/tool-trace.js +5 -1
- package/dist/orchestration/workflow-agent-session-index.d.ts +85 -2
- package/dist/orchestration/workflow-agent-session-index.js +38 -5
- package/dist/parked-decide.d.ts +40 -1
- package/dist/parked-decide.js +19 -2
- package/dist/plugins/checkpoint-store-sql.js +5 -5
- package/dist/plugins/local-checkpoint-store.js +3 -3
- package/dist/plugins/remote-env-adb.d.ts +11 -0
- package/dist/plugins/remote-env-adb.js +2 -0
- package/dist/plugins/remote-env-device.d.ts +9 -0
- package/dist/plugins/remote-env-device.js +3 -0
- package/dist/plugins/remote-env-e2b.d.ts +11 -0
- package/dist/plugins/remote-env-e2b.js +2 -0
- package/dist/plugins/remote-env-host.d.ts +11 -0
- package/dist/plugins/remote-env-host.js +2 -0
- package/dist/plugins/remote-env-k8s.d.ts +11 -0
- package/dist/plugins/remote-env-k8s.js +2 -0
- package/dist/plugins/remote-env-local-docker.d.ts +11 -0
- package/dist/plugins/remote-env-local-docker.js +2 -0
- package/dist/plugins/remote-env-ssh.d.ts +11 -0
- package/dist/plugins/remote-env-ssh.js +2 -0
- package/dist/plugins/store-backend.d.ts +36 -13
- package/dist/plugins/store-backend.js +12 -3
- package/dist/plugins/store-contracts.d.ts +54 -18
- package/dist/plugins/store-contracts.js +17 -30
- package/dist/plugins/workflow-journal-store-sql.d.ts +1 -1
- package/dist/plugins/workflow-journal-store-sql.js +3 -10
- package/dist/plugins/workflow-run-store-sql.d.ts +31 -9
- package/dist/plugins/workflow-run-store-sql.js +28 -1
- package/dist/posture-source.d.ts +23 -3
- package/dist/posture-source.js +1 -1
- package/dist/read-face-posture.d.ts +8 -1
- package/dist/read-face-posture.js +1 -0
- package/dist/run-cancel-context.d.ts +50 -1
- package/dist/run-cancel-context.js +22 -0
- package/dist/run-local.d.ts +14 -6
- package/dist/run-local.js +4 -1
- package/dist/runs.d.ts +0 -51
- package/dist/runs.js +40 -25
- package/dist/task-a2a.js +2 -2
- package/dist/task-mcp.js +2 -2
- package/dist/task-settings.d.ts +21 -8
- package/dist/task-settings.js +2 -2
- package/dist/tool-approval.d.ts +14 -8
- package/dist/tool-approval.js +3 -3
- package/dist/trace/core-keyset-guard.d.ts +135 -11
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +1 -0
- package/dist/trace/ledger-events.d.ts +22 -2
- package/dist/trace/ledger-events.js +6 -1
- package/dist/trace/ledger-sink.d.ts +10 -8
- package/dist/trace/ledger-sink.js +10 -2
- package/dist/trace/project.d.ts +24 -6
- package/dist/trace/project.js +5 -3
- package/dist/trace/redact.d.ts +28 -0
- package/dist/trace/redact.js +58 -30
- package/package.json +3 -3
package/dist/config-types.d.ts
CHANGED
|
@@ -55,6 +55,24 @@ export interface RetentionSweepConfig {
|
|
|
55
55
|
* 那道拒之后它即刻生效 —— 留着是为了「那天不必有人再猜一个数」。
|
|
56
56
|
*/
|
|
57
57
|
export declare const MEMORY_CONSOLIDATION_MIN_INTERVAL_SEC = 300;
|
|
58
|
+
/**
|
|
59
|
+
* S-202 —— **首 token 看门狗的缺省上限(ms)**,全仓单一属主。
|
|
60
|
+
*
|
|
61
|
+
* 「SSE 已开但迟迟不吐第一个 delta」是唯一一条 core 的 idle 计时器够不着的挂死路(它只在首 token
|
|
62
|
+
* 之后才武装),所以这条看门狗是那条路上唯一的守卫。600s 而不是原来的 120s:自托管后端在长 prefill
|
|
63
|
+
* (大上下文 / 冷缓存 / 排队)下,首字合理地就是要等几分钟——120s 会把一次**正常**的慢启动判成
|
|
64
|
+
* `[network]` 失败去重试,而重试再交同一份 prompt 给同一个排队中的后端,只会更慢更贵。
|
|
65
|
+
*
|
|
66
|
+
* ⚠️ **`0 = 关`语义不变**,提高缺省不碰它;依赖 120s 快失败的部署把
|
|
67
|
+
* `MODEL_FIRST_TOKEN_TIMEOUT_MS=120000` 显式写回即可(env 恒赢)。
|
|
68
|
+
*
|
|
69
|
+
* 🔴 **常量在此、别处只许引用**:解析器腿(`config.ts` 的 `numEnv` 缺省)、目录腿
|
|
70
|
+
* (`config-catalog.ts` 的 `staticDefault`)与 `brain.ts` 的 `DEFAULT_RESILIENCE`(config 对象没带
|
|
71
|
+
* resilience 块时的同姿态兜底)此前各写一份 `120000` 字面量,改一处漏一处 = 对外宣告的缺省与真跑的
|
|
72
|
+
* 不是同一代。`test/config-catalog.test.ts` 的 S-202 两格咬:一格钉三腿同值,一格扫全 `src/` 判
|
|
73
|
+
* 「字面量」这一类写法为红(下一份镜像出生即红)。
|
|
74
|
+
*/
|
|
75
|
+
export declare const MODEL_FIRST_TOKEN_TIMEOUT_MS_DEFAULT = 600000;
|
|
58
76
|
/**
|
|
59
77
|
* [ref] D1/D2 —— consolidation driver 阀门的三根旋钮(语义与三问见
|
|
60
78
|
* {@link ServiceConfigFlat.memoryConsolidationDriver})。纯数据。
|
|
@@ -608,6 +626,18 @@ export interface ServiceConfigFlat {
|
|
|
608
626
|
* snapshot). Self-hosted sandbox lane; auth = explicit K8S_* env or in-cluster service account.
|
|
609
627
|
* - `ssh` → a real host over SSH (NOT isolated/suspendable; [ref] §5). AI-orchestrated batch deploy.
|
|
610
628
|
* - `adb` → a real device over ADB (NOT isolated/suspendable). On-device APP testing. */
|
|
629
|
+
/**
|
|
630
|
+
* S-213④ —— `REMOTE_EXEC_HOME_DIR`:**执行环境的 home 目录**(core `ExecutionEnv.homeDir`),
|
|
631
|
+
* `~/…` 形权限规则(`Edit(~/.ssh/**)`)唯一的基,绝对路径。
|
|
632
|
+
*
|
|
633
|
+
* 部署级旋钮,**跨车道同一根**(不按 provider 分七根 —— 它回答的是同一个问题:「这条部署的手,
|
|
634
|
+
* 跑在哪个用户的 home 下」)。显式声明**恒赢**;缺席时只有 home 属于车道本身的那几条车道有答案
|
|
635
|
+
* (host/in-process = `os.homedir()`、e2b = 平台固定用户),k8s/local-docker(镜像属性)、
|
|
636
|
+
* ssh(目标机登录用户)、adb/device 一律缺席 ⇒ `~/` 规则在下单时被 422 响亮拒。
|
|
637
|
+
* 派生与逐车道理由的**单一属主** = `execution-lane-caps.ts` 的 `executionLaneHomeDir`。
|
|
638
|
+
* 非绝对路径 ⇒ **拒启**([ref] A 档;core 侧同判为 `config.execution_env_home_dir_invalid`)。
|
|
639
|
+
*/
|
|
640
|
+
remoteExecHomeDir?: string;
|
|
611
641
|
remoteExec?: {
|
|
612
642
|
provider: "e2b";
|
|
613
643
|
apiKey: string;
|
|
@@ -1721,7 +1751,7 @@ export type ServiceMemoryConfig = Pick<ServiceConfigFlat, "memoryEngineEnabled"
|
|
|
1721
1751
|
* config 这一层它只是一句声明。 */
|
|
1722
1752
|
export type ServiceAuthConfig = Pick<ServiceConfigFlat, "authToken" | "authTokens" | "allowUnauthedWrites" | "corsOrigins" | "principalHeader" | "requirePrincipal" | "autonomy" | "commandPolicy" | "compliancePosture" | "lockedConfigKeys" | "configCatalogEnabled" | "retentionPolicy" | "retentionSweep" | "operatorPrincipals" | "principalJwtPubkeys" | "principalJwtIss" | "principalJwtAud" | "principalJwtMaxTtlSec" | "bindHost" | "bindHostSource">;
|
|
1723
1753
|
/** 组:orchestration(编排 + 执行车道 + 沙箱面 + workflow/后台 agent 存留)。 */
|
|
1724
|
-
export type ServiceOrchestrationConfig = Pick<ServiceConfigFlat, "remoteExec" | "worktreeIsolation" | "leaderEnabled" | "leaderFanoutEnabled" | "routerEnabled" | "selfOrchestrationEnabled" | "selfOrchestrationModels" | "selfOrchestrationWorkerIsolation" | "forkEnabled" | "experimentalObserverAgents" | "schedulerEnabled" | "schedulerSessionWakeup" | "schedulerSessionLifetime" | "schedulerStorePath" | "planModeEnabled" | "hooksTimeoutMs" | "workflowRunStoreBackend" | "workflowOrphanGraceMs" | "workflowJournalRetentionMs" | "workflowRunRetentionMs" | "usageWindows" | "workflowAgentsReadOnly" | "workflowSizeGuideline" | "backgroundAgentRetentionMs" | "backgroundAgentStaleRunningMs" | "backgroundAgentParkClaimStaleMs" | "delegationEntryCaps" | "rosterRetentionMs" | "leaderRunStaleMs" | "scratchpadSweepTtlMs" | "sandboxPkgSource" | "sessionAutoTitle" | "sessionAutoTitleSource" | "selectEnvironmentTool" | "envFactsEnabled" | "toolDeferLongtail" | "lspEnabled" | "lspHostEnabled" | "imageBakes" | "readFace" | "readFaceSource" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude">;
|
|
1754
|
+
export type ServiceOrchestrationConfig = Pick<ServiceConfigFlat, "remoteExec" | "remoteExecHomeDir" | "worktreeIsolation" | "leaderEnabled" | "leaderFanoutEnabled" | "routerEnabled" | "selfOrchestrationEnabled" | "selfOrchestrationModels" | "selfOrchestrationWorkerIsolation" | "forkEnabled" | "experimentalObserverAgents" | "schedulerEnabled" | "schedulerSessionWakeup" | "schedulerSessionLifetime" | "schedulerStorePath" | "planModeEnabled" | "hooksTimeoutMs" | "workflowRunStoreBackend" | "workflowOrphanGraceMs" | "workflowJournalRetentionMs" | "workflowRunRetentionMs" | "usageWindows" | "workflowAgentsReadOnly" | "workflowSizeGuideline" | "backgroundAgentRetentionMs" | "backgroundAgentStaleRunningMs" | "backgroundAgentParkClaimStaleMs" | "delegationEntryCaps" | "rosterRetentionMs" | "leaderRunStaleMs" | "scratchpadSweepTtlMs" | "sandboxPkgSource" | "sessionAutoTitle" | "sessionAutoTitleSource" | "selectEnvironmentTool" | "envFactsEnabled" | "toolDeferLongtail" | "lspEnabled" | "lspHostEnabled" | "imageBakes" | "readFace" | "readFaceSource" | "readDenyPatterns" | "readDenyBuiltinTiers" | "readDenyBuiltinExclude">;
|
|
1725
1755
|
/** 组:limitsHttp(HTTP 面 + 各类上限/配额/回收窗)。 */
|
|
1726
1756
|
export type ServiceLimitsHttpConfig = Pick<ServiceConfigFlat, "port" | "attachmentOrphanGraceMs" | "workspaceFileMaxBytes" | "attachmentMaxBytes" | "attachmentMimeAllowlist" | "attachmentUnboundTtlMs" | "infraCostRates" | "drainGraceMs" | "sighupIdleGraceMs" | "parentPid" | "engineAutoExit" | "engineLingerMs" | "rateLimitPerMin" | "maxTaskCostUsd" | "maxTaskTokens" | "maxPrincipalCostUsd" | "costQuotaWindowSec" | "reapIntervalSec" | "runStaleSec" | "toolResultTtlSec">;
|
|
1727
1757
|
/** 组:observability(可观测)。 */
|
package/dist/config-types.js
CHANGED
package/dist/config.js
CHANGED
|
@@ -9,7 +9,7 @@ import { DEFAULT_ELICITATION_THROTTLE } from "./elicitation.js";
|
|
|
9
9
|
import { isV2ScopeKey } from "./memory-scope.js";
|
|
10
10
|
import { DEFAULT_QUESTION_THROTTLE } from "./question.js";
|
|
11
11
|
import { GIT_API_KINDS, isGitApiKind } from "./git-api-kind.js";
|
|
12
|
-
import { RETENTION_MODES, MEMORY_CONSOLIDATION_MIN_INTERVAL_SEC } from "./config-types.js";
|
|
12
|
+
import { RETENTION_MODES, MEMORY_CONSOLIDATION_MIN_INTERVAL_SEC, MODEL_FIRST_TOKEN_TIMEOUT_MS_DEFAULT } from "./config-types.js";
|
|
13
13
|
import { buildWriteProtectionPosture } from "./write-protection.js";
|
|
14
14
|
import { normalizeSshFingerprint } from "./ssh-host-key.js";
|
|
15
15
|
import { readFaceForPosture } from "./read-face-posture.js";
|
|
@@ -856,7 +856,7 @@ function parseModelDomain() {
|
|
|
856
856
|
: undefined,
|
|
857
857
|
resilience: {
|
|
858
858
|
connectTimeoutMs: numEnv("MODEL_CONNECT_TIMEOUT_MS", "30000"),
|
|
859
|
-
firstTokenTimeoutMs: numEnv("MODEL_FIRST_TOKEN_TIMEOUT_MS",
|
|
859
|
+
firstTokenTimeoutMs: numEnv("MODEL_FIRST_TOKEN_TIMEOUT_MS", String(MODEL_FIRST_TOKEN_TIMEOUT_MS_DEFAULT)),
|
|
860
860
|
idleTimeoutMs: numEnv("MODEL_IDLE_TIMEOUT_MS", "300000"),
|
|
861
861
|
circuitBreaker: boolEnv("MODEL_CIRCUIT_BREAKER", false),
|
|
862
862
|
failureThreshold: numEnv("MODEL_CB_FAILURE_THRESHOLD", "5"),
|
|
@@ -1551,6 +1551,18 @@ function parseOrchestrationDomain(ctx) {
|
|
|
1551
1551
|
assertReadDenyBuiltinsResolvable(tiers, exclude);
|
|
1552
1552
|
return { ...(tiers !== undefined ? { readDenyBuiltinTiers: tiers } : {}), ...(exclude !== undefined ? { readDenyBuiltinExclude: exclude } : {}) };
|
|
1553
1553
|
})(),
|
|
1554
|
+
...((h) => (h !== undefined ? { remoteExecHomeDir: h } : {}))((() => {
|
|
1555
|
+
const raw = process.env.REMOTE_EXEC_HOME_DIR;
|
|
1556
|
+
if (raw === undefined || raw === "")
|
|
1557
|
+
return undefined;
|
|
1558
|
+
if (!raw.startsWith("/")) {
|
|
1559
|
+
throw new Error(`REMOTE_EXEC_HOME_DIR="${raw}" is not an absolute path — it is the home directory of the user the EXECUTION ` +
|
|
1560
|
+
`environment runs as (the base a \`~/\` permission rule resolves against), so a relative value cannot be ` +
|
|
1561
|
+
`resolved and would silently guard nothing. Spell it absolutely (e.g. /home/user), or unset it to have ` +
|
|
1562
|
+
`\`~/\` rules refused loudly on this lane.`);
|
|
1563
|
+
}
|
|
1564
|
+
return raw;
|
|
1565
|
+
})()),
|
|
1554
1566
|
remoteExec: process.env.REMOTE_EXEC === "e2b" && process.env.E2B_API_KEY
|
|
1555
1567
|
? {
|
|
1556
1568
|
provider: "e2b",
|
|
@@ -1941,7 +1953,7 @@ const AUTH_GROUP_KEYS = [
|
|
|
1941
1953
|
"principalJwtMaxTtlSec", "bindHost", "bindHostSource",
|
|
1942
1954
|
];
|
|
1943
1955
|
const ORCHESTRATION_GROUP_KEYS = [
|
|
1944
|
-
"remoteExec", "worktreeIsolation", "leaderEnabled", "leaderFanoutEnabled", "routerEnabled", "selfOrchestrationEnabled",
|
|
1956
|
+
"remoteExec", "remoteExecHomeDir", "worktreeIsolation", "leaderEnabled", "leaderFanoutEnabled", "routerEnabled", "selfOrchestrationEnabled",
|
|
1945
1957
|
"selfOrchestrationModels", "selfOrchestrationWorkerIsolation", "forkEnabled", "experimentalObserverAgents",
|
|
1946
1958
|
"schedulerEnabled", "schedulerSessionWakeup", "schedulerSessionLifetime", "schedulerStorePath", "planModeEnabled", "hooksTimeoutMs", "workflowRunStoreBackend",
|
|
1947
1959
|
"workflowOrphanGraceMs", "workflowJournalRetentionMs", "workflowRunRetentionMs", "usageWindows", "workflowAgentsReadOnly",
|
|
@@ -59,8 +59,10 @@ export interface PathAdjudication {
|
|
|
59
59
|
* 形上是一个永不创建的哨兵目录 —— 对写门 fail-safe,对规则 fail-open。理由逐字见
|
|
60
60
|
* `task-settings.ts` 的 `FsWriteGateWiring.taskRoot`。缺席 ⇒ 需要基的规则拼法被 core 整表拒(fail-loud)。 */
|
|
61
61
|
readonly taskRoot?: string;
|
|
62
|
-
/** 🔴 S-177 / codex r2
|
|
63
|
-
*
|
|
62
|
+
/** 🔴 S-177 / codex r2 + S-213④:**执行环境的 home**(`~/…` 形规则的基)。在**本部署的执行车道
|
|
63
|
+
* 真知道自己的 home 时**在场 —— 判据的唯一属主是 `execution-lane-caps.ts` 的 `executionLaneHomeDir`
|
|
64
|
+
* (adapter 的 `ExecutionEnv.homeDir` 座读的是同一个函数)。绝不回落引擎进程自己的 home:远端车道上
|
|
65
|
+
* 那是另一个用户的目录,拿它去匹配 = 一条收紧规则静默守错地方。逐字见
|
|
64
66
|
* `task-settings.ts` 的 `FsWriteGateWiring.taskHome`。 */
|
|
65
67
|
readonly taskHome?: string;
|
|
66
68
|
}
|
|
@@ -85,7 +87,8 @@ export interface DurableApprovalSeat {
|
|
|
85
87
|
* durable resume 驱动、断连后的 detach 腿)ask——durable park 原语义逐字保留([ref] 后无活流腿放行执行
|
|
86
88
|
* 也不会产出空答:coordinator 无 ALS ctx ⇒ 冻结 `{kind:"unavailable"}`(src/question.ts),永不悬挂;
|
|
87
89
|
* park 仍是把问题送到人面前的唯一那条腿,正当性不变、只是反事实前提换了)。
|
|
88
|
-
*
|
|
90
|
+
* 工具名取 `approval-content-kind.ts` 的单一属主(它 re-export core 根导出的 `ASK_USER_QUESTION_TOOL_NAME`);
|
|
91
|
+
* server.ts 的 pre-CAS 守卫读同一枚(S-213①:此处旧注称该常量「core 未根导出」= 失实,曾据此手抄 13 处)。
|
|
89
92
|
* ⚠️ **单一属主**(复审 A2):AskUserQuestion 的 durable 判决只有这一处。任何需要「同参重建」这条判决的
|
|
90
93
|
* 地方(main.ts 的 parkedReviveInheritedGate 父约束链)必须调本工厂,不得自折 core 原形——两份拷贝里
|
|
91
94
|
* 只改一份正是本条 finding 的成因。**登记豁免一处**:leader worker 腿(src/leader/wire.ts provisionWorker)
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { join, posix } from "node:path";
|
|
2
2
|
import { FileError, StubExecutionEnv, combinePolicies, createAllowDenyPolicy, createDurableQuestionPolicy, createSensitivePathPolicy, createTranscriptIntegrityPolicy, err, ok, } from "@sema-agent/core";
|
|
3
3
|
import { createDurableAskPolicy } from "./approval.js";
|
|
4
|
+
import { ASK_USER_QUESTION_TOOL_NAME } from "./approval-content-kind.js";
|
|
4
5
|
export function createDurableQuestionGate(live) {
|
|
5
6
|
if (live === undefined)
|
|
6
7
|
return createDurableQuestionPolicy();
|
|
7
8
|
return {
|
|
8
9
|
check(req) {
|
|
9
|
-
if (req.toolName !==
|
|
10
|
+
if (req.toolName !== ASK_USER_QUESTION_TOOL_NAME)
|
|
10
11
|
return { action: "allow" };
|
|
11
12
|
return live.hasLiveContext()
|
|
12
13
|
? { action: "allow" }
|
|
@@ -1,40 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 执行车道**闭集**与 per-lane 能力表(design device-executor-lane-v2 §4.1 收敛件,车A-1)。
|
|
3
|
-
*
|
|
4
|
-
* ## 根本问题:lane 词表是开放字符串上的十几处独立判别式
|
|
5
|
-
*
|
|
6
|
-
* `config.remoteExec?.provider` 的静态型是 `string | undefined` 的判别联合,但消费面从来不是一处
|
|
7
|
-
* switch —— 而是散在十几个文件里的**独立等式/集合/否定**:`cwdHonored` 比 `=== "host"`、
|
|
8
|
-
* `isRemoteScratchpadLane` 查一个 `Set`、`isSandboxPathAdjudicationLane` 取 `!== "host"` 的否定、
|
|
9
|
-
* `sandboxSendLaneEnabled` 是一串 if。于是「lane 词表加一个词」的病形不是编译错,是**每一处的默认
|
|
10
|
-
* 答案静默错**:新词不在 Set 里 ⇒ 静默 false;不等于 `"host"` ⇒ 静默 true。没有一处会报,每一处都
|
|
11
|
-
* 给出一个**没有人表过态**的答案(DISEASE-SHAPES V1/V2;[ref] 执行车道轴的同族病)。
|
|
12
|
-
*
|
|
13
|
-
* ## 本模块 = 那张表的单源(三件)
|
|
14
|
-
*
|
|
15
|
-
* ① {@link ExecutionLane}:闭集联合。含 `in-process` —— `REMOTE_EXEC` 未设的进程内 stub 形**是**一条
|
|
16
|
-
* 真车道(`boot/resolve-spec.ts` 的 `?? "in-process"`、`config.ts` 的落点记账都已在用这个词),
|
|
17
|
-
* 把它排除在闭集外,普查就少一列。
|
|
18
|
-
* ② {@link executionLaneCaps}:**穷举 switch**(先例 = `capabilities/hands-lane.ts` 的
|
|
19
|
-
* `pickHandsRunner`:无 `default` 臂 ⇒ 联合加词而此处不加臂 = 编译错,而不是运行期落一个默认行)。
|
|
20
|
-
* ③ {@link ExecutionLaneCaps} 的每一位都点名它**今天的判别式坐标**,由对账测试
|
|
21
|
-
* `test/execution-lane-caps.test.ts` 逐位跑真判别式与本表对拍。
|
|
22
|
-
*
|
|
23
|
-
* ## 🔴 本模块是表,不是执法者
|
|
24
|
-
*
|
|
25
|
-
* 消费点今天仍各自判别(把判别式改成读本表 = 分批的后续车)。所以本表的全部价值押在对账测试上:
|
|
26
|
-
* 表与判别式一旦分叉,门红。**唯一**允许的分叉是 {@link PLANNED_LANE_DIVERGENCES} 里逐条登记的
|
|
27
|
-
* 「表是目标态、判别式待某车补」;登记项之外的分叉是漂移,登记项被补齐而不销行同样是红(双向)。
|
|
28
|
-
*
|
|
29
|
-
* ## 能力位的口径
|
|
30
|
-
*
|
|
31
|
-
* 每一位回答的是**车道结构上能不能**,不是「这个部署开没开」:旋钮(`LSP_ENABLED` /
|
|
32
|
-
* `SCHEDULER_ENABLED` / k8s 的 `s3Snapshot`)由消费点在表之上再合取。租户面是唯一的例外 —— 它与
|
|
33
|
-
* 车道语义绑死(host 的单用户前提),所以进了 {@link LaneCapability} 的第三个词而不是靠调用方记住。
|
|
34
|
-
*
|
|
35
|
-
* 依赖纪律:本文件是**叶**(只 type-import config-types),不引 config.ts —— config.ts 反过来引它取
|
|
36
|
-
* {@link RemoteExecProvider}([ref] 必需 env 表按同一联合键控)。
|
|
37
|
-
*/
|
|
38
1
|
import type { ServiceConfigFlat } from "./config-types.js";
|
|
39
2
|
/** 一位能力的三态。`single-user-only` = 车道支持,但只在单用户部署(`requirePrincipal !== true`)开 ——
|
|
40
3
|
* 多租户下这一位必须关(host 家族的 cwd/scheduler/backgroundShell/LSP 都是这一档,理由同源:
|
|
@@ -149,4 +112,55 @@ export interface PlannedLaneDivergence {
|
|
|
149
112
|
* 加行的门槛不变:一条分叉必须写清 today / owner / why 三件,并在补齐的**同一批**里销掉。
|
|
150
113
|
*/
|
|
151
114
|
export declare const PLANNED_LANE_DIVERGENCES: readonly PlannedLaneDivergence[];
|
|
115
|
+
/**
|
|
116
|
+
* E2B 沙箱里 agent 跑成的那个用户的 home。
|
|
117
|
+
*
|
|
118
|
+
* 这是 **E2B 平台的事实**,不是本仓的猜:E2B 的基础模板建 `user` 这个用户,每个 sandbox 的进程都以它
|
|
119
|
+
* 运行,home 恒 `/home/user`;本仓 adapter 自己的工作区根默认值(`plugins/remote-env-e2b.ts` 的
|
|
120
|
+
* `DEFAULT_MOUNT_PATH`)用的就是同一个目录。**若某个部署的自定义模板换了用户**,运维用
|
|
121
|
+
* `REMOTE_EXEC_HOME_DIR` 显式声明覆盖 —— 那条旋钮恒赢,见 {@link executionLaneHomeDir}。
|
|
122
|
+
* ⚠️ 本值**未经真机实测**(本批无 e2b 真机),按平台约定 + 仓内既有默认常量登记。
|
|
123
|
+
*/
|
|
124
|
+
export declare const E2B_SANDBOX_HOME = "/home/user";
|
|
125
|
+
/** {@link executionLaneHomeDir} 读的那两位部署配置(`ServiceConfig` 结构上满足)。 */
|
|
126
|
+
export interface LaneHomeDeployment {
|
|
127
|
+
readonly remoteExec?: {
|
|
128
|
+
readonly provider: RemoteExecProvider;
|
|
129
|
+
} | undefined;
|
|
130
|
+
/** `REMOTE_EXEC_HOME_DIR` —— 运维显式声明的执行环境 home(绝对路径;坏值在 config 层拒启)。 */
|
|
131
|
+
readonly remoteExecHomeDir?: string | undefined;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* 「**这条部署的执行环境**以哪个 home 运行」—— `~/…` 形权限规则(`Edit(~/.ssh/**)`)唯一的基。
|
|
135
|
+
*
|
|
136
|
+
* ## 病(接入审计 M13 / DEBTS S-213④)
|
|
137
|
+
* core 的 `ExecutionEnv.homeDir` 是**由 adapter 声明**的座(`engine/harness/types.d.ts:511`,可选):
|
|
138
|
+
* 缺席 ⇒ core 的 `resolvePathPattern` 回 `{missingBase:"home"}` ⇒ 该规则对这条腿判 `unreadable`
|
|
139
|
+
* (fail-closed 的 ask)。四只远端 adapter(e2b/k8s/host/ssh)一个都没声明,而 server 侧的 422 前置门
|
|
140
|
+
* (`task-settings.ts` 的 `rejectUnmatchableSettingsNames`)在 home 未知时整表拒 `~/` 形 ——
|
|
141
|
+
* **于是远端部署根本写不出 `~/` 规则**,恒 422。
|
|
142
|
+
*
|
|
143
|
+
* ## 本函数 = 那个值的**单一属主**
|
|
144
|
+
* 两个消费面读同一份,不许各算各的(两处不同 = 一条规则在 422 门上被拒、却在引擎里编得出来,或反之):
|
|
145
|
+
* ① 每只 adapter 的 `homeDir` 座(`boot/execution-env.ts` 把本函数的答案递进 adapter 配置);
|
|
146
|
+
* ② 规则基 `PathRuleBases.home`(`boot/resolve-spec.ts` / `boot/parked-revive-gate.ts` 的
|
|
147
|
+
* `pathAdjudication.taskHome`)。
|
|
148
|
+
*
|
|
149
|
+
* ## 一条规则,不是七条特判
|
|
150
|
+
* **运维显式声明恒赢**(`REMOTE_EXEC_HOME_DIR`,部署级旋钮,不挂任何客户端表态);没有声明时,
|
|
151
|
+
* 只有「home 是**车道本身**的属性」的那几条车道才有答案:
|
|
152
|
+
* · `host` / `in-process` —— 命令就跑在本进程这台机器、这个用户下,`os.homedir()` 读的就是它
|
|
153
|
+
* (core 自己的 `NodeExecutionEnv` 同判);
|
|
154
|
+
* · `e2b` —— 平台固定用户,见 {@link E2B_SANDBOX_HOME};
|
|
155
|
+
* · `k8s` / `local-docker` —— home 是**镜像**的属性,boot 期无从得知;
|
|
156
|
+
* · `ssh` —— home 是**目标机上那个登录用户**的属性(root 是 `/root`、macOS 是 `/Users/<u>`),
|
|
157
|
+
* 连上之前无从得知,而 422 门要在下单那一刻回答;
|
|
158
|
+
* · `adb` —— Android 没有 POSIX 意义上的登录 home;
|
|
159
|
+
* · `device` —— 员工自己的机器,运行期才连进来。
|
|
160
|
+
* 这五条一律**缺席**(诚实:不猜一个目录去守)。缺席的后果是 `~/` 规则在下单时被 422 响亮拒,
|
|
161
|
+
* 与 core 的 `missingBase:"home"` 同向 —— 这正是负控要的那一格。
|
|
162
|
+
*
|
|
163
|
+
* 认不出的车道 ⇒ `undefined`(调用方 fail-closed)。生产链上不可达:`loadConfig` 的 [ref] 拒启门先咬。
|
|
164
|
+
*/
|
|
165
|
+
export declare function executionLaneHomeDir(deployment: LaneHomeDeployment): string | undefined;
|
|
152
166
|
//# sourceMappingURL=execution-lane-caps.d.ts.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
1
2
|
const NO = "unsupported";
|
|
2
3
|
const YES = "supported";
|
|
3
4
|
const SU = "single-user-only";
|
|
@@ -163,4 +164,26 @@ export function laneCapabilityHolds(cap, deployment) {
|
|
|
163
164
|
}
|
|
164
165
|
}
|
|
165
166
|
export const PLANNED_LANE_DIVERGENCES = Object.freeze([]);
|
|
167
|
+
export const E2B_SANDBOX_HOME = "/home/user";
|
|
168
|
+
export function executionLaneHomeDir(deployment) {
|
|
169
|
+
const declared = deployment.remoteExecHomeDir;
|
|
170
|
+
if (declared !== undefined)
|
|
171
|
+
return declared;
|
|
172
|
+
const lane = executionLaneOf(deployment.remoteExec?.provider);
|
|
173
|
+
if (lane === undefined)
|
|
174
|
+
return undefined;
|
|
175
|
+
switch (lane) {
|
|
176
|
+
case "in-process":
|
|
177
|
+
case "host":
|
|
178
|
+
return homedir();
|
|
179
|
+
case "e2b":
|
|
180
|
+
return E2B_SANDBOX_HOME;
|
|
181
|
+
case "k8s":
|
|
182
|
+
case "local-docker":
|
|
183
|
+
case "ssh":
|
|
184
|
+
case "adb":
|
|
185
|
+
case "device":
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
166
189
|
//# sourceMappingURL=execution-lane-caps.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { TaskNotificationPayload, TerminalCause, WorkflowRun } from "@sema-agent/core";
|
|
2
|
-
import type { RunRecord } from "../plugins/store-contracts.js";
|
|
2
|
+
import type { RunRecord, UnsuccessfulRunStatus } from "../plugins/store-contracts.js";
|
|
3
|
+
import type { AssertAllKeysHandled } from "../trace/core-keyset-guard.js";
|
|
3
4
|
/** [ref] 幽灵行案的单源判别:一条 `task_notification` 只有在 **agent 族 × 终态** 时才允许打
|
|
4
5
|
* `onChildTerminal`(fleet「subagent 树」只渲 agent 子代)。`background_bash`/`monitor`/`external`
|
|
5
6
|
* 不属 agent fleet 树——它们此前每条都打,fleet-bus 的「无 tick 无 claim」臂给 b\* 与 m\* handle 合成
|
|
@@ -618,4 +619,24 @@ export declare function isTerminalFleetStatus(status: FleetTaskStatus): boolean;
|
|
|
618
619
|
export declare function runStatusToFleet(status: RunStatusForFleet, opts?: {
|
|
619
620
|
awaitingPlanApproval?: boolean;
|
|
620
621
|
}): FleetTaskStatus;
|
|
622
|
+
/**
|
|
623
|
+
* S-213③ —— **编译期同源钉**:fleet 的 `"failed"` 桶必须恰好收下 `RUN_STATUS_CLASS` 判为「非成功终局」
|
|
624
|
+
* 的每一个 core 词。
|
|
625
|
+
*
|
|
626
|
+
* 这个词的病史:`blocked` 曾在本仓有**两个独立写者**且答案互斥 —— 本文件读它为「非成功终局(agent
|
|
627
|
+
* 放弃)⇒ 行离场」,而 `observability/run-terminal-log.ts` 读它为「治理判决,不是失败 ⇒ 不打行」。两处
|
|
628
|
+
* 各自穷举、各自正确地编译,结果是一条 `report_blocked` 的 run 在面板上离场、在引擎日志里一个字都没有。
|
|
629
|
+
* 语义收归 `plugins/store-contracts.ts` 的那张表之后,本行不再自持判断:表把某个词挪出/挪进
|
|
630
|
+
* `unsuccessful`(或 core 新增一个非成功终态)而上面的 switch 没跟上 ⇒ 下面这条收不敛到 `never` ⇒ tsc 红。
|
|
631
|
+
* (`"error"` 是 server 自有别名、不在那张表的辖域内,故不在本钉的两边出现。)
|
|
632
|
+
*/
|
|
633
|
+
type FleetUnsuccessfulBucket = "failed" | "blocked";
|
|
634
|
+
type _GuardFleetUnsuccessfulBucket = AssertAllKeysHandled<Exclude<UnsuccessfulRunStatus, FleetUnsuccessfulBucket>>;
|
|
635
|
+
/** 反向门(与 `core-keyset-guard.ts` 的 `_Guard*Reverse` 同形):桶里留了一个表**不再**判为非成功终局的
|
|
636
|
+
* 词同样红 —— 只有正向门时,把 `blocked` 挪回 `succeeded` 而 switch 照旧塞进 failed 桶,并集覆盖、
|
|
637
|
+
* 差集门一声不吭(本批实测:只加正向门时该变异 tsc rc=0)。两道钉一起加。 */
|
|
638
|
+
type _GuardFleetUnsuccessfulBucketReverse = AssertAllKeysHandled<Exclude<FleetUnsuccessfulBucket, UnsuccessfulRunStatus>>;
|
|
639
|
+
/** 占位使用(防 noUnusedLocals 形 lint;类型别名本身零运行时)。 */
|
|
640
|
+
export type FleetVocabGuards = [_GuardFleetUnsuccessfulBucket, _GuardFleetUnsuccessfulBucketReverse];
|
|
641
|
+
export {};
|
|
621
642
|
//# sourceMappingURL=fleet-bus.d.ts.map
|
package/dist/fleet/fleet-bus.js
CHANGED
|
@@ -634,9 +634,10 @@ export function runStatusToFleet(status, opts) {
|
|
|
634
634
|
case "completed":
|
|
635
635
|
return "completed";
|
|
636
636
|
case "failed":
|
|
637
|
-
case "error":
|
|
638
637
|
case "blocked":
|
|
639
638
|
return "failed";
|
|
639
|
+
case "error":
|
|
640
|
+
return "failed";
|
|
640
641
|
case "suspended":
|
|
641
642
|
return "waiting";
|
|
642
643
|
case "needs_review":
|
|
@@ -8,6 +8,7 @@ import { principalFrom, decodeCheckpointScope, PRINCIPAL_TOKEN_HEADER, APPROVAL_
|
|
|
8
8
|
import { verifyDirectDoorProof } from "../../principal-jwt.js";
|
|
9
9
|
import { MAX_APPROVAL_REASON_CHARS } from "../../approval-hmac.js";
|
|
10
10
|
import { redactedPreview, redactSecrets } from "../../trace/redact.js";
|
|
11
|
+
import { redactRiskDescriptor } from "../../observability/run-terminal-log.js";
|
|
11
12
|
import { fleetRunLabels } from "../../fleet/fleet-bus.js";
|
|
12
13
|
import { sleep } from "../sse-log.js";
|
|
13
14
|
import { bindSseLifecycle } from "../sse-lifecycle.js";
|
|
@@ -546,18 +547,8 @@ function redactPendingDisclosures(row) {
|
|
|
546
547
|
const rd = row.riskDescriptor;
|
|
547
548
|
if (rd === null || rd === undefined)
|
|
548
549
|
return row;
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
if (shadowedRule === undefined && summary === undefined)
|
|
552
|
-
return row;
|
|
553
|
-
return {
|
|
554
|
-
...row,
|
|
555
|
-
riskDescriptor: {
|
|
556
|
-
...rd,
|
|
557
|
-
...(shadowedRule !== undefined ? { shadowedRule } : {}),
|
|
558
|
-
...(summary !== undefined ? { summary } : {}),
|
|
559
|
-
},
|
|
560
|
-
};
|
|
550
|
+
const next = redactRiskDescriptor(rd);
|
|
551
|
+
return next === rd ? row : { ...row, riskDescriptor: next };
|
|
561
552
|
}
|
|
562
553
|
function projectPendingForWire(row, governance) {
|
|
563
554
|
const redacted = redactPendingDisclosures(row);
|
|
@@ -10,21 +10,25 @@ export function buildServerWiringGates(input) {
|
|
|
10
10
|
backend: input.backend,
|
|
11
11
|
parkFacility: input.checkpointStore !== undefined,
|
|
12
12
|
});
|
|
13
|
+
const noCenterArm = (knob) => `this knob has no config-center arm on this deployment — it is set by ${knob} on this machine, or derived from the deployment posture`;
|
|
13
14
|
return {
|
|
14
15
|
streamApproval: gate.active ? "active" : gate.reason,
|
|
15
16
|
durableApproval: buildPostureKnobReading(input.durableApproval, {
|
|
16
17
|
knob: "DURABLE_APPROVAL",
|
|
17
18
|
source: input.durableApprovalSource,
|
|
19
|
+
centerMeaning: noCenterArm("DURABLE_APPROVAL"),
|
|
18
20
|
postureMeaning: "derived from the single-user turnkey posture (REQUIRE_PRINCIPAL is not set) composed with a durable store being present: park/resume and the /v1/approvals routes are on by default — set DURABLE_APPROVAL=false to pin them off",
|
|
19
21
|
}),
|
|
20
22
|
streamAskWindowMs: buildPostureKnobReading(input.streamAskWindowMs, {
|
|
21
23
|
knob: "STREAM_ASK_WINDOW_MS",
|
|
22
24
|
source: input.streamAskWindowMsSource,
|
|
25
|
+
centerMeaning: noCenterArm("STREAM_ASK_WINDOW_MS"),
|
|
23
26
|
postureMeaning: "derived from the single-user turnkey posture (REQUIRE_PRINCIPAL is not set): an approval card waits 24h instead of 5 minutes, so stepping away does not settle the run unattended — set STREAM_ASK_WINDOW_MS to pin your own window",
|
|
24
27
|
}),
|
|
25
28
|
sessionAutoTitle: buildPostureKnobReading(input.sessionAutoTitle, {
|
|
26
29
|
knob: "SESSION_AUTO_TITLE",
|
|
27
30
|
source: input.sessionAutoTitleSource,
|
|
31
|
+
centerMeaning: noCenterArm("SESSION_AUTO_TITLE"),
|
|
28
32
|
postureMeaning: "this knob has no posture arm — its default is the same on every deployment shape",
|
|
29
33
|
}),
|
|
30
34
|
checkpointStore: input.checkpointStore !== undefined,
|
package/dist/http/routes/runs.js
CHANGED
|
@@ -24,7 +24,7 @@ import { normalizeRunEventType } from "../../trace/project.js";
|
|
|
24
24
|
import { sendJson, sendError, httpErrorCode, sseHeaders } from "../send.js";
|
|
25
25
|
import { buildActiveRunConflict } from "../active-run-conflict.js";
|
|
26
26
|
import { clearTurnActivity, readTurnActivityMs } from "../../turn-activity.js";
|
|
27
|
-
import {
|
|
27
|
+
import { mintCancelledResult } from "../../run-cancel-context.js";
|
|
28
28
|
import { buildCrossSliceUsage, readResourceWindow, recordResourceWindow } from "../../resource-window.js";
|
|
29
29
|
import { headerStr, gatedPrincipal, explicitOperatorOk } from "../principal-gate.js";
|
|
30
30
|
import { ActorAssertionWire } from "@sema-agent/settings-schema";
|
|
@@ -331,8 +331,7 @@ async function handleRunsBody(req, res, url, ctx, miss) {
|
|
|
331
331
|
}
|
|
332
332
|
const err = note;
|
|
333
333
|
try {
|
|
334
|
-
const
|
|
335
|
-
const result = { taskId, sessionId: run.sessionId, terminal: { kind: "failed", code: "cancelled", message: err }, stats: { turns: 0, tokens: 0 }, ...(cancelContext ? { cancelContext } : {}) };
|
|
334
|
+
const result = mintCancelledResult({ taskId, sessionId: run.sessionId, startedAtMs: Date.parse(run.createdAt), message: err });
|
|
336
335
|
let lastErr;
|
|
337
336
|
for (let attempt = 0;; attempt++) {
|
|
338
337
|
try {
|
|
@@ -9,7 +9,7 @@ import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotific
|
|
|
9
9
|
import { createLedgerSink } from "../../trace/ledger-sink.js";
|
|
10
10
|
import { registerEngineNoticeLeg } from "../../trace/engine-notice-wire.js";
|
|
11
11
|
import { clearTurnActivity, readTurnActivityMs, recordTurnActivity } from "../../turn-activity.js";
|
|
12
|
-
import {
|
|
12
|
+
import { mintCancelledResult } from "../../run-cancel-context.js";
|
|
13
13
|
import { recordResourceWindow } from "../../resource-window.js";
|
|
14
14
|
import { redactSecrets } from "../../trace/redact.js";
|
|
15
15
|
import { contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, wiringManifestOperatorEventData, toolRosterDeltaEventData, humanInputEventData, appendModelUsageDelta, attachModelUsage } from "../../trace/project.js";
|
|
@@ -190,17 +190,13 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
190
190
|
let runRowSettled = false;
|
|
191
191
|
let durableTaskId;
|
|
192
192
|
let durableHeartbeat;
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
stats: { turns: 0, tokens: 0 },
|
|
201
|
-
...(ctx ? { cancelContext: ctx } : {}),
|
|
202
|
-
};
|
|
203
|
-
};
|
|
193
|
+
const cancelledResult = (errorMessage, engineResult) => mintCancelledResult({
|
|
194
|
+
taskId: durableTaskId ?? "",
|
|
195
|
+
sessionId: prepared.spec.sessionId ?? "",
|
|
196
|
+
startedAtMs: streamLegStartedAt,
|
|
197
|
+
message: errorMessage,
|
|
198
|
+
...(engineResult !== undefined ? { engineResult } : {}),
|
|
199
|
+
});
|
|
204
200
|
let userMsgEntryId;
|
|
205
201
|
let anchorPut = false;
|
|
206
202
|
const putRewindAnchor = () => {
|
|
@@ -398,12 +394,13 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
398
394
|
break;
|
|
399
395
|
if (ev.type === "message_committed" && ev.role === "user")
|
|
400
396
|
userMsgEntryId = ev.entryId;
|
|
397
|
+
const cancelVerbAtDone = ev.type === "done" && durableTaskId !== undefined && cancelledViaVerb.has(durableTaskId);
|
|
401
398
|
if (ev.type === "done") {
|
|
402
399
|
finalResult = redactTerminalResult(stripResumeToken(await graftParkToolCallId(ev.result, deps.checkpointStore)));
|
|
403
400
|
if (durableTaskId && finalResult)
|
|
404
401
|
finalResult = { ...finalResult, taskId: durableTaskId };
|
|
405
|
-
if (
|
|
406
|
-
finalResult =
|
|
402
|
+
if (cancelVerbAtDone && finalResult !== undefined) {
|
|
403
|
+
finalResult = cancelledResult("cancelled by user", finalResult);
|
|
407
404
|
}
|
|
408
405
|
finalizeTaskResult(ev.result, principal, prepared.spec.objective, prepared.spec.sessionId);
|
|
409
406
|
noteRunOutcome(finalResult);
|
|
@@ -644,7 +641,7 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
644
641
|
}
|
|
645
642
|
else {
|
|
646
643
|
const dcErr = "stream client disconnected before completion (run aborted)";
|
|
647
|
-
const dc =
|
|
644
|
+
const dc = cancelledResult(dcErr);
|
|
648
645
|
if (ledgerSink && !ledgerTerminal) {
|
|
649
646
|
ledgerTerminal = true;
|
|
650
647
|
await ledgerSink.flush().catch(() => undefined);
|
|
@@ -696,7 +693,7 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
696
693
|
}
|
|
697
694
|
else if (ac.signal.aborted) {
|
|
698
695
|
const err = "stream aborted before completion (run cancelled)";
|
|
699
|
-
const c =
|
|
696
|
+
const c = cancelledResult(err);
|
|
700
697
|
if (ledgerSink && !ledgerTerminal) {
|
|
701
698
|
ledgerTerminal = true;
|
|
702
699
|
await ledgerSink.flush().catch(() => undefined);
|
|
@@ -745,6 +742,7 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
745
742
|
else {
|
|
746
743
|
const withDiscoveryNotice = (body) => prepared.verify || prepared.cascade ? { ...body, notice: MULTI_ATTEMPT_NO_EVENT_CHANNEL_NOTICE } : body;
|
|
747
744
|
const runSyncSubmitLeg = async () => {
|
|
745
|
+
const syncLegStartedAt = Date.now();
|
|
748
746
|
let durableTaskId;
|
|
749
747
|
let durableHeartbeat;
|
|
750
748
|
if (deps.runStore && prepared.spec.sessionId) {
|
|
@@ -808,8 +806,14 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
808
806
|
cancelledViaVerb.delete(durableTaskId);
|
|
809
807
|
}
|
|
810
808
|
}
|
|
811
|
-
if (cancelVerbLabel
|
|
812
|
-
result = {
|
|
809
|
+
if (cancelVerbLabel) {
|
|
810
|
+
result = mintCancelledResult({
|
|
811
|
+
taskId: durableTaskId ?? "",
|
|
812
|
+
sessionId: prepared.spec.sessionId ?? "",
|
|
813
|
+
startedAtMs: syncLegStartedAt,
|
|
814
|
+
message: "cancelled by user",
|
|
815
|
+
engineResult: result,
|
|
816
|
+
});
|
|
813
817
|
}
|
|
814
818
|
const resumeAtStatus = resumeAtHttpStatus(result);
|
|
815
819
|
if (resumeAtStatus) {
|
package/dist/http/server.js
CHANGED
|
@@ -3,7 +3,7 @@ import { redactErrorMessage, redactHead } from "../observability/run-terminal-lo
|
|
|
3
3
|
import { once } from "node:events";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
import { uuidv7, isThinkingLevel, expandTiers, resumeWithVerification, CheckpointError, defaultTaskRegistry, validatePendingSteer, subscribeWorkflow, GLOBAL_USAGE_KEY, usageRetryAfterMs, terminalProjection, PAUSE_REGISTRY } from "@sema-agent/core";
|
|
6
|
-
import { decideParkedAgent, findParkOrigin, planWorkflowParkedDecide } from "../parked-decide.js";
|
|
6
|
+
import { decideParkedAgent, denyWorkflowParkIdentityLost, findParkOrigin, planWorkflowParkedDecide } from "../parked-decide.js";
|
|
7
7
|
import { isModelAllowlisted, matchCatalogModel } from "../model-select.js";
|
|
8
8
|
import {} from "../config-center/facade.js";
|
|
9
9
|
import { HttpError, principalFrom, verifiedPrincipal, setSsoPrincipal, ssoVerifiedPrincipal, setSsoScope, isUuidV7, isDestructiveSessionWrite, decodeCheckpointScope, CHECKPOINT_PUBLIC_SCOPE, PRINCIPAL_TOKEN_HEADER, APPROVAL_MAC_HEADER, APPROVAL_MAC_KID_HEADER } from "../security.js";
|
|
@@ -17,6 +17,7 @@ import { normalizeApproachNotice, unknownTaskAgentModelRef, validateTaskAgents }
|
|
|
17
17
|
import { isValidCwd, MAX_ADDITIONAL_DIRS } from "../task-cwd.js";
|
|
18
18
|
import { runInBackground, evictIfConflict, parkToolCallId, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort } from "../runs.js";
|
|
19
19
|
import { failureCodeOf, nestedPauseOf, stripResumeToken } from "../terminal.js";
|
|
20
|
+
import { mintCancelledResult } from "../run-cancel-context.js";
|
|
20
21
|
import { personDecision } from "../host-decision.js";
|
|
21
22
|
import { readTurnActivityMs, recordTurnActivity } from "../turn-activity.js";
|
|
22
23
|
import { recordResumeResourceWindow } from "../resource-window.js";
|
|
@@ -32,7 +33,7 @@ import { TERMINAL_PARK_GATE_KINDS } from "../plugins/checkpoint-store-sql.js";
|
|
|
32
33
|
import { createWallClockJumpGuard } from "../wall-clock-jump-guard.js";
|
|
33
34
|
import {} from "../observability/rate-limit.js";
|
|
34
35
|
import { withPrincipal } from "../observability/principal-context.js";
|
|
35
|
-
import { turnEndEventData, contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, humanInputEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "../trace/project.js";
|
|
36
|
+
import { turnEndEventData, contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, humanInputEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "../trace/project.js";
|
|
36
37
|
import { redactSecrets } from "../trace/redact.js";
|
|
37
38
|
import { createLedgerSink, createSerialLedgerAppend } from "../trace/ledger-sink.js";
|
|
38
39
|
import { registerEngineNoticeLeg } from "../trace/engine-notice-wire.js";
|
|
@@ -84,6 +85,7 @@ export { coarseStatusForState, errorCodeForExit };
|
|
|
84
85
|
import { sendJson, sendError, httpErrorCode, msg, isSseResponse } from "./send.js";
|
|
85
86
|
import { authorized, systemFor, gatedPrincipal, explicitOperatorOk, isOperator } from "./principal-gate.js";
|
|
86
87
|
import { buildActiveRunConflict, resumeEntryForGate } from "./active-run-conflict.js";
|
|
88
|
+
import { ASK_USER_QUESTION_TOOL_NAME } from "../approval-content-kind.js";
|
|
87
89
|
export { explicitOperatorOk, isOperator };
|
|
88
90
|
export function flattenServiceDeps(deps) {
|
|
89
91
|
const { stores, coordinators, seams, observability, governance, deployment, knobs, ...flat } = deps;
|
|
@@ -1142,7 +1144,7 @@ export function createHttpServer(rawDeps) {
|
|
|
1142
1144
|
};
|
|
1143
1145
|
}
|
|
1144
1146
|
const paForGrant = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction : undefined;
|
|
1145
|
-
const loadedGrantTool = paForGrant !== undefined && paForGrant.toolName && paForGrant.toolName !==
|
|
1147
|
+
const loadedGrantTool = paForGrant !== undefined && paForGrant.toolName && paForGrant.toolName !== ASK_USER_QUESTION_TOOL_NAME
|
|
1146
1148
|
? paForGrant.toolName
|
|
1147
1149
|
: null;
|
|
1148
1150
|
const boundGrant = onResumeCommitted
|
|
@@ -1207,6 +1209,10 @@ export function createHttpServer(rawDeps) {
|
|
|
1207
1209
|
noteLateSettleIfAccepted(parked);
|
|
1208
1210
|
return parked;
|
|
1209
1211
|
}
|
|
1212
|
+
if (origin?.kind === "workflow_identity_lost") {
|
|
1213
|
+
void ctxPromise.catch(() => { });
|
|
1214
|
+
return denyWorkflowParkIdentityLost(origin);
|
|
1215
|
+
}
|
|
1210
1216
|
if (origin?.kind === "workflow") {
|
|
1211
1217
|
void ctxPromise.catch(() => { });
|
|
1212
1218
|
const plan = planWorkflowParkedDecide(origin, parkedReq);
|
|
@@ -1255,10 +1261,10 @@ export function createHttpServer(rawDeps) {
|
|
|
1255
1261
|
throw e;
|
|
1256
1262
|
}
|
|
1257
1263
|
const pendingTool = cp.pendingAction.toolName;
|
|
1258
|
-
if (decision === "approve" && pendingTool ===
|
|
1264
|
+
if (decision === "approve" && pendingTool === ASK_USER_QUESTION_TOOL_NAME && !answer) {
|
|
1259
1265
|
return { status: 400, body: { error: "pending action is AskUserQuestion — approve must carry body.answer ({ answers: [{ header, selected: string[], note? }] })", errorCode: "request.field_conflict" } };
|
|
1260
1266
|
}
|
|
1261
|
-
if (answer && (decision !== "approve" || pendingTool !==
|
|
1267
|
+
if (answer && (decision !== "approve" || pendingTool !== ASK_USER_QUESTION_TOOL_NAME)) {
|
|
1262
1268
|
return { status: 400, body: { error: `body.answer is only valid when approving a pending AskUserQuestion (decision: "${decision}", pending: "${pendingTool ?? "unknown"}")`, errorCode: "request.field_conflict" } };
|
|
1263
1269
|
}
|
|
1264
1270
|
const { objective: resumeObjective, sessionId: _sessionId, ...taskConfig } = spec;
|
|
@@ -1306,6 +1312,7 @@ export function createHttpServer(rawDeps) {
|
|
|
1306
1312
|
return await Promise.race([accepted, leg]);
|
|
1307
1313
|
}
|
|
1308
1314
|
async function driveResumeLeg(args, onAccepted) {
|
|
1315
|
+
const resumeLegStartedAt = Date.now();
|
|
1309
1316
|
const { runner: legRunner, token, sessionId, principal, fleetScope, taskConfig, resumeObjective, outcome, verifyRounds } = args;
|
|
1310
1317
|
const legKey = createHash("sha256").update(String(token)).digest("hex");
|
|
1311
1318
|
const streamApprovalOn = resolveStreamApprovalGate({
|
|
@@ -1689,6 +1696,13 @@ export function createHttpServer(rawDeps) {
|
|
|
1689
1696
|
await flush();
|
|
1690
1697
|
await append("wiring_manifest", wiringManifestEventData(ev));
|
|
1691
1698
|
break;
|
|
1699
|
+
case "tool_roster_delta": {
|
|
1700
|
+
await flush();
|
|
1701
|
+
const rosterDelta = toolRosterDeltaEventData(ev);
|
|
1702
|
+
if (rosterDelta !== undefined)
|
|
1703
|
+
await append("tool_roster_delta", rosterDelta);
|
|
1704
|
+
break;
|
|
1705
|
+
}
|
|
1692
1706
|
case "human_input":
|
|
1693
1707
|
await flush();
|
|
1694
1708
|
await append("human_input", humanInputEventData(ev));
|
|
@@ -1797,7 +1811,7 @@ export function createHttpServer(rawDeps) {
|
|
|
1797
1811
|
}
|
|
1798
1812
|
}
|
|
1799
1813
|
if (claimedRow && taskId && deps.runStore) {
|
|
1800
|
-
const c = { taskId, sessionId,
|
|
1814
|
+
const c = mintCancelledResult({ taskId, sessionId, startedAtMs: resumeLegStartedAt, message: e.message });
|
|
1801
1815
|
await deps.runStore.setTerminal(taskId, "failed", c, e.message).catch(() => undefined);
|
|
1802
1816
|
}
|
|
1803
1817
|
settleFleet("failed");
|
|
@@ -1852,7 +1866,7 @@ export function createHttpServer(rawDeps) {
|
|
|
1852
1866
|
}
|
|
1853
1867
|
if (cancelCtrl.signal.aborted) {
|
|
1854
1868
|
if (taskId && deps.runStore) {
|
|
1855
|
-
const c = { taskId, sessionId,
|
|
1869
|
+
const c = mintCancelledResult({ taskId, sessionId, startedAtMs: resumeLegStartedAt, message: "cancelled by user" });
|
|
1856
1870
|
await deps.runStore.setTerminal(taskId, "failed", c, "cancelled by user").catch(() => undefined);
|
|
1857
1871
|
}
|
|
1858
1872
|
settleFleet("failed");
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { loadConfig, type ServiceConfig } from "./config.js";
|
|
2
2
|
export { createBrain } from "./brain.js";
|
|
3
3
|
export { createSessionStore } from "./plugins/session-store.js";
|
|
4
|
-
export { createStoreBackend, type StoreBackend } from "./plugins/store-backend.js";
|
|
4
|
+
export { createStoreBackend, type StoreBackend, type SessionCaptureRecordSource } from "./plugins/store-backend.js";
|
|
5
5
|
export { CachingSessionStore, type CachingOptions } from "./plugins/caching-session-store.js";
|
|
6
6
|
export type { RunRecord, RunEvent } from "./plugins/store-contracts.js";
|
|
7
7
|
export { runInBackground } from "./runs.js";
|