@sema-agent/server 7.76.2 → 7.78.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/runner-deps.js +3 -1
- package/dist/config-catalog.js +1 -0
- package/dist/config-center/read-warnings.d.ts +10 -1
- package/dist/config-center/read-warnings.js +5 -1
- package/dist/config-provider.js +2 -2
- package/dist/config-types.d.ts +13 -1
- package/dist/config.js +2 -1
- package/dist/fleet/subagent-tail-bus.d.ts +28 -2
- package/dist/fleet/subagent-tail-bus.js +24 -2
- package/dist/http/principal-gate.d.ts +19 -0
- package/dist/http/principal-gate.js +7 -0
- package/dist/http/routes/sessions.js +47 -6
- package/dist/http/routes/tasks.js +25 -11
- package/dist/http/routes/workflows.js +7 -1
- package/dist/http/server.js +18 -23
- package/dist/observability/fail-open.d.ts +8 -0
- package/dist/observability/fail-open.js +8 -0
- package/dist/observability/run-terminal-log.js +1 -0
- package/dist/orchestration/workflow-agent-session-index.d.ts +7 -3
- package/dist/parked-decide.d.ts +27 -12
- package/dist/parked-decide.js +2 -2
- package/dist/plugins/file-run-store.d.ts +6 -1
- package/dist/plugins/file-run-store.js +10 -0
- package/dist/plugins/memory-run-store.d.ts +6 -1
- package/dist/plugins/memory-run-store.js +10 -0
- package/dist/plugins/run-store-sql.d.ts +13 -2
- package/dist/plugins/run-store-sql.js +5 -0
- package/dist/plugins/store-contracts.d.ts +16 -0
- package/dist/plugins/workflow-run-store-sql.d.ts +7 -3
- package/dist/runs.js +4 -10
- package/dist/trace/core-keyset-guard.d.ts +7 -7
- package/dist/trace/ledger-events.d.ts +7 -1
- package/dist/trace/ledger-sink.js +10 -15
- package/dist/trace/model-segment.d.ts +55 -0
- package/dist/trace/model-segment.js +58 -0
- package/dist/trace/project.d.ts +75 -0
- package/dist/trace/project.js +123 -3
- package/dist/trace/redact.js +163 -17
- package/package.json +4 -4
package/dist/boot/runner-deps.js
CHANGED
|
@@ -156,7 +156,9 @@ export function createRunnerDeps(ctx) {
|
|
|
156
156
|
onElicit: elicitation ? elicitation.elicit : undefined,
|
|
157
157
|
onQuestion: question ? question.question : undefined,
|
|
158
158
|
onAsk: createRunnerDepsOnAsk(toolApproval),
|
|
159
|
-
autoMode: {
|
|
159
|
+
autoMode: {
|
|
160
|
+
...(config.autoModeClassifierFallback !== undefined ? { classifierFallback: config.autoModeClassifierFallback } : {}),
|
|
161
|
+
},
|
|
160
162
|
sessionStore,
|
|
161
163
|
memoryBackend: memoryEngine ? memoryEngine.backend : undefined,
|
|
162
164
|
memoryEngineDir: memoryEngine ? memoryEngine.root : undefined,
|
package/dist/config-catalog.js
CHANGED
|
@@ -73,6 +73,7 @@ export const CONFIG_CATALOG = [
|
|
|
73
73
|
r("AUTH_BRIDGE_ISSUER", "auth", "string", "registry SSO-JWT issuer(auth bridge 的开关之一)", { danger: SEC, derivedDefaultNote: "缺席 ⇒ 用 configCenter.baseUrl(配了 center 即默认开桥)" }),
|
|
74
74
|
r("AUTH_BRIDGE_JWKS_URL", "auth", "url", "registry SSO-JWT 的 JWKS 地址(缺省从 issuer 派生)", { danger: SEC }),
|
|
75
75
|
r("AUTONOMY", "auth", "enum", "运维 autonomy 模式,tighten-only 叠到每个 TaskSpec(read-only/plan ⇒ 只读手,ask ⇒ 恒 shellGate);center 腿热且 non-sticky", { danger: SEC, enumValues: ["read-only", "ask", "plan", "auto"], configKey: "autonomy", center: cGov("autonomy") }),
|
|
76
|
+
r("AUTO_MODE_CLASSIFIER_FALLBACK", "approval", "enum", "auto 分类器席答不出裁决时的梯子(main=主模型代判 / none=只留分类器那一级,整条拒)", { enumValues: ["main", "none"], danger: SEC, derivedDefaultNote: "未设/空串 ⇒ 不铸键,跟随引擎缺省 main(core 7.18.0 起;空串按未设,与其余词表旋钮同判);none = 7.77.0 及以前的行为逐字;词表外的值启动期拒启。权限轴放宽,升级须知见 DEPLOY-PREREQS", configKey: "autoModeClassifierFallback" }),
|
|
76
77
|
r("BAKE_CACHE_BASE", "orchestration", "url", "烤镜像 fetch-once 缓存源 base(注入 --cache-base;绝不受调用方控制)"),
|
|
77
78
|
r("BAKE_IMAGE_REGISTRY", "orchestration", "string", "烤镜像自动登记的 registry host 前缀(repo=registry/profile)"),
|
|
78
79
|
r("BAKE_LEASE_MS", "orchestration", "number", "bake 租约窗(ms;runner 心跳推进,过期被 reaper 抢)", { staticDefault: "90000" }),
|
|
@@ -9,12 +9,21 @@
|
|
|
9
9
|
*
|
|
10
10
|
* 截断:schema 的键列表有界(64),超界带 `truncatedFrom`(真实总数)——文案如实带上,消费方永远不把有界列表当穷举。
|
|
11
11
|
* 词表是闭集:`switch` 穷举 `kind`,schema 加新 kind 这里编译红,不会静默漏臂。
|
|
12
|
+
*
|
|
13
|
+
* **归族只有一条判据**(下游只有一个消费者——候选门:非空 ⇒ 这棵树整拒、保 LKG):
|
|
14
|
+
* 运维**写了而没生效**的 ⇒ 候选门错误;值**已被收编接受**(写了、生效了、只是换了个位置或被夹住)⇒ 不入诊断面。
|
|
15
|
+
* settings-schema 2.0.0 的两员新告警各自入族,没有新开第三档:`entry-dropped`(逐条容错丢掉的那条条目——
|
|
16
|
+
* 运维写下的模型/服务器没进目录,与 `domain-defaulted` 是同一件事的细/粗两档)归前者;
|
|
17
|
+
* `legacy-governance-lifted`(0.5 期 runtime 文档里的治理三闸被抬进 governance 域)归后者——那几个键**生效了**
|
|
18
|
+
* (`buildEffective` 用抬升结果解析 governance),把它折成错误会让一份合法的 0.5 期存量配置每一次 refresh 都被
|
|
19
|
+
* 候选门整拒,而它在 1.10.0 上零告警零 domainErrors(亲验读数)。兼容臂的关窗观测归属主(settings-schema 的
|
|
20
|
+
* `onWarning` 流),不借候选门这条带副作用的通道传信息。
|
|
12
21
|
*/
|
|
13
22
|
import type { EffectiveReadWarning } from "@sema-agent/settings-schema";
|
|
14
23
|
export type FoldedReadWarning = {
|
|
15
24
|
readonly domain: string;
|
|
16
25
|
readonly error: string;
|
|
17
26
|
};
|
|
18
|
-
/** null = 不入诊断面(`hosts-grandfathered`:值已被收编接受,与本地店同口径)。 */
|
|
27
|
+
/** null = 不入诊断面(`hosts-grandfathered` / `legacy-governance-lifted`:值已被收编接受,与本地店同口径)。 */
|
|
19
28
|
export declare function foldEffectiveReadWarning(w: EffectiveReadWarning): FoldedReadWarning | null;
|
|
20
29
|
//# sourceMappingURL=read-warnings.d.ts.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const MAX_ERROR_CHARS = 600;
|
|
2
2
|
const cap = (s) => s.slice(0, MAX_ERROR_CHARS);
|
|
3
|
+
const errorText = (e) => (e instanceof Error ? e.message : String(e));
|
|
3
4
|
const keyList = (prefix, keys, truncatedFrom) => {
|
|
4
5
|
const total = truncatedFrom ?? keys.length;
|
|
5
6
|
const budget = MAX_ERROR_CHARS - prefix.length;
|
|
@@ -18,9 +19,12 @@ const keyList = (prefix, keys, truncatedFrom) => {
|
|
|
18
19
|
export function foldEffectiveReadWarning(w) {
|
|
19
20
|
switch (w.kind) {
|
|
20
21
|
case "hosts-grandfathered":
|
|
22
|
+
case "legacy-governance-lifted":
|
|
21
23
|
return null;
|
|
22
24
|
case "domain-defaulted":
|
|
23
|
-
return { domain: w.domain, error: cap(
|
|
25
|
+
return { domain: w.domain, error: cap(errorText(w.error)) };
|
|
26
|
+
case "entry-dropped":
|
|
27
|
+
return { domain: w.domain, error: cap(`entry dropped by domain schema: ${w.path} (${errorText(w.error)})`) };
|
|
24
28
|
case "unknown-keys-dropped":
|
|
25
29
|
return { domain: w.domain, error: keyList("unknown keys dropped by domain schema: ", w.keys, w.truncatedFrom) };
|
|
26
30
|
case "unknown-keys-carried":
|
package/dist/config-provider.js
CHANGED
|
@@ -2,7 +2,7 @@ import { readMcpSourceLabel } from "./mcp-source-label.js";
|
|
|
2
2
|
import { createHmac, randomBytes } from "node:crypto";
|
|
3
3
|
import { foldEffectiveReadWarning } from "./config-center/read-warnings.js";
|
|
4
4
|
import { FileConfigStore } from "@sema-agent/settings-schema/node";
|
|
5
|
-
import { findSkillContent, skillContentHash, refIntegrityIssues,
|
|
5
|
+
import { findSkillContent, skillContentHash, refIntegrityIssues, createSiblingResolver, } from "@sema-agent/settings-schema";
|
|
6
6
|
import { redactSecrets } from "./trace/redact.js";
|
|
7
7
|
import { fetchEffective as remoteFetchEffective, fetchSkillContent as remoteFetchSkillContent, ConfigCenterHttpError, } from "./config-center/facade.js";
|
|
8
8
|
import { createLogger } from "./observability/logger.js";
|
|
@@ -74,7 +74,7 @@ export class LocalConfigProvider {
|
|
|
74
74
|
if (etag !== undefined && etag === tag)
|
|
75
75
|
return null;
|
|
76
76
|
const refErrors = [];
|
|
77
|
-
const resolve =
|
|
77
|
+
const resolve = createSiblingResolver(store, eff);
|
|
78
78
|
for (const d of ["rosters", "workers", "models", "collab", "systems", "entitlement"]) {
|
|
79
79
|
const issues = await refIntegrityIssues(d, eff[d], resolve);
|
|
80
80
|
if (issues.length > 0)
|
package/dist/config-types.d.ts
CHANGED
|
@@ -1440,6 +1440,18 @@ export interface ServiceConfigFlat {
|
|
|
1440
1440
|
* 「缺席=不阻」⇒ 壳发 `permissionMode:"auto"` 即武装)。词表外的值 boot 拒启(安全轴旋钮不许静默回默认)。
|
|
1441
1441
|
* 合成点 = `boot/runtime-caps.ts` 最外层(applyLocalAutoModeDeny);读面 = `/v1/capabilities.permissionModeAuto`。 */
|
|
1442
1442
|
permissionsDisableAutoMode: boolean;
|
|
1443
|
+
/** 7.78.0(core 7.18.0 [ref])—— **分类器席答不出话时怎么办**(`AUTO_MODE_CLASSIFIER_FALLBACK`,
|
|
1444
|
+
* 部署级旋钮,闭二词 `main` | `none`,词表外的值 boot 拒启)。
|
|
1445
|
+
*
|
|
1446
|
+
* 为什么这一格必须由部署说了算:core 7.18.0 把**缺省**从「分类器跑不出裁决 ⇒ 这一次门控调用整条拒」
|
|
1447
|
+
* 改成「主模型代判」(`main`)。auto 模式里的分类器是一道**筛子**,而 `main` 让**被筛的那只模型**
|
|
1448
|
+
* 兼任筛子 —— 这是权限轴上的一次放宽,愿不愿意接受它是部署的事,不是引擎的事。`none` = 只留分类器
|
|
1449
|
+
* 那一级梯子 = **7.77.0 及以前的行为逐字**。
|
|
1450
|
+
*
|
|
1451
|
+
* 🔴 **缺席 = 不铸键**(与 `readFace` 族同律,不复制上游缺省):上游改缺省之日起本仓不会成为第二份
|
|
1452
|
+
* 真源。所以缺席时这台机器跟随 core 的 `main`,运维面由 DEPLOY-PREREQS 的升级段点名(「默认变了」
|
|
1453
|
+
* 是升级须知,不是一个可以静默过去的实现细节)。 */
|
|
1454
|
+
autoModeClassifierFallback?: "main" | "none";
|
|
1443
1455
|
/** S-111([ref] §4.4)—— CC `settings.crossSessionInbound` 的**部署/组织层**(引擎层形的 `managed` 层,
|
|
1444
1456
|
* CC `policySettings` 对位):本部署对入站跨会话消息的治理三态。`accept` 投递 / `hold` 停在收件会话的待审
|
|
1445
1457
|
* 队列 / `refuse` 整体退出车道。**未设 = 本层不设**(不是 accept):引擎读作「managed 层未表态」,由其余层
|
|
@@ -1814,7 +1826,7 @@ export type ServiceStoreConfig = Pick<ServiceConfigFlat, "sessionBackend" | "ses
|
|
|
1814
1826
|
export type ServiceModelPlaneConfig = Pick<ServiceConfigFlat, "gatewayBaseUrl" | "gatewayApiKey" | "gatewayFallbackUrls" | "gatewayMaxRetries" | "anthropic" | "resilience" | "model" | "envModel" | "models" | "modelApiKeyEnv" | "modelApiKeys" | "modelQuotaWeights" | "tiers" | "projects" | "roles" | "atModelAllowlist" | "cascadeLadder" | "degrade">;
|
|
1815
1827
|
/** 组:approval(审批 / HITL 门)。`directDoorActive` 无 env 解析腿(装配层三域合取的产物),但语义上
|
|
1816
1828
|
* 就是本组的门状态,故进组;`parseApprovalDomain` 的返回类型相应是 `Omit<…, "directDoorActive">`。 */
|
|
1817
|
-
export type ServiceApprovalConfig = Pick<ServiceConfigFlat, "approvalRequire" | "approvalDeny" | "approvalTimeoutSec" | "approvalAutoBudget" | "approvalNeverAuto" | "approvalHmacKeys" | "durableApproval" | "durableApprovalSource" | "streamAskWindowMsSource" | "directApprovalDoor" | "directDoorActive" | "resourceSuspend" | "resourceSuspendTtlSec" | "resourceSuspendTotalTokens" | "resourceSuspendTotalBudgetUsd" | "resourceSuspendMaxSlices" | "askQuestionEnabled" | "questionThrottle" | "toolApprovalEnabled" | "permissionRulesEnabled" | "permissionRulesEnabledExplicit" | "streamAskWindowMarginMs" | "streamApproval" | "unattendedApprovalPolicy" | "mcpElicitation" | "sensitiveWritePatterns" | "writeProtectedPaths" | "manualModeShellGate" | "permissionsDisableAutoMode" | "crossSessionInbound" | "crossSessionDialogExpiry">;
|
|
1829
|
+
export type ServiceApprovalConfig = Pick<ServiceConfigFlat, "approvalRequire" | "approvalDeny" | "approvalTimeoutSec" | "approvalAutoBudget" | "approvalNeverAuto" | "approvalHmacKeys" | "durableApproval" | "durableApprovalSource" | "streamAskWindowMsSource" | "directApprovalDoor" | "directDoorActive" | "resourceSuspend" | "resourceSuspendTtlSec" | "resourceSuspendTotalTokens" | "resourceSuspendTotalBudgetUsd" | "resourceSuspendMaxSlices" | "askQuestionEnabled" | "questionThrottle" | "toolApprovalEnabled" | "permissionRulesEnabled" | "permissionRulesEnabledExplicit" | "streamAskWindowMarginMs" | "streamApproval" | "unattendedApprovalPolicy" | "mcpElicitation" | "sensitiveWritePatterns" | "writeProtectedPaths" | "manualModeShellGate" | "permissionsDisableAutoMode" | "autoModeClassifierFallback" | "crossSessionInbound" | "crossSessionDialogExpiry">;
|
|
1818
1830
|
/** 组:memory(记忆面 + TOC 同步腿)。 */
|
|
1819
1831
|
export type ServiceMemoryConfig = Pick<ServiceConfigFlat, "memoryEngineEnabled" | "memoryEngineDir" | "memoryEngineRemoteLaneAllowed" | "memoryEngineBackend" | "memoryScope" | "memoryPersistenceCapable" | "memorySync" | "memoryEmbedder" | "memoryOrgAdmissionMode" | "memoryDelegationEvidence" | "memoryProvenance" | "memoryCapturePolicy" | "memoryConsolidationDriver" | "memoryOrgDirectoryJson" | "memoryOrgGrantTtlMs" | "memoryOrgUnavailableBackoffMs" | "projectMemoryEnabled" | "syncImportLeaseStaleSec">;
|
|
1820
1832
|
/** 组:auth(鉴权 / 身份 / 治理棒)。`commandPolicy` 只有 sema-registry 腿(无 env 标量形),故 env 解析
|
package/dist/config.js
CHANGED
|
@@ -1162,6 +1162,7 @@ function parseApprovalDomain(ctx) {
|
|
|
1162
1162
|
`Refusing to boot rather than treating a misspelled value as "unset": that silently ran the deployment with NO shell gate while the operator believed one was configured`);
|
|
1163
1163
|
})(process.env.MANUAL_MODE_SHELL_GATE),
|
|
1164
1164
|
permissionsDisableAutoMode: boolEnv("PERMISSIONS_DISABLE_AUTO_MODE", false),
|
|
1165
|
+
...optionalEnumKey("autoModeClassifierFallback", "AUTO_MODE_CLASSIFIER_FALLBACK", process.env.AUTO_MODE_CLASSIFIER_FALLBACK, ["main", "none"]),
|
|
1165
1166
|
...optionalEnumKey("crossSessionInbound", "CROSS_SESSION_INBOUND", process.env.CROSS_SESSION_INBOUND, CROSS_SESSION_INBOUND_SETTINGS),
|
|
1166
1167
|
...optionalEnumKey("crossSessionDialogExpiry", "CROSS_SESSION_DIALOG_EXPIRY", process.env.CROSS_SESSION_DIALOG_EXPIRY, CROSS_SESSION_DIALOG_EXPIRY_VALUES),
|
|
1167
1168
|
mcpElicitation: {
|
|
@@ -1993,7 +1994,7 @@ const APPROVAL_GROUP_KEYS = [
|
|
|
1993
1994
|
"approvalRequire", "approvalDeny", "approvalTimeoutSec", "approvalAutoBudget", "approvalNeverAuto",
|
|
1994
1995
|
"approvalHmacKeys", "durableApproval", "durableApprovalSource", "streamAskWindowMsSource", "directApprovalDoor", "directDoorActive", "resourceSuspend",
|
|
1995
1996
|
"resourceSuspendTtlSec", "resourceSuspendTotalTokens", "resourceSuspendTotalBudgetUsd", "resourceSuspendMaxSlices", "askQuestionEnabled", "questionThrottle", "toolApprovalEnabled", "permissionRulesEnabled", "permissionRulesEnabledExplicit", "streamAskWindowMarginMs", "streamApproval", "unattendedApprovalPolicy", "mcpElicitation",
|
|
1996
|
-
"sensitiveWritePatterns", "writeProtectedPaths", "manualModeShellGate", "permissionsDisableAutoMode", "crossSessionInbound", "crossSessionDialogExpiry",
|
|
1997
|
+
"sensitiveWritePatterns", "writeProtectedPaths", "manualModeShellGate", "permissionsDisableAutoMode", "autoModeClassifierFallback", "crossSessionInbound", "crossSessionDialogExpiry",
|
|
1997
1998
|
];
|
|
1998
1999
|
const MEMORY_GROUP_KEYS = [
|
|
1999
2000
|
"memoryEngineEnabled", "memoryEngineDir", "memoryEngineRemoteLaneAllowed", "memoryEngineBackend", "memoryScope",
|
|
@@ -7,7 +7,9 @@ export type SubagentTailFrame = Record<string, unknown> & {
|
|
|
7
7
|
type: string;
|
|
8
8
|
};
|
|
9
9
|
/** core forward 事件 → wire 投影(sync 主流五分支同源;不认识的帧类型=null 不发——tail 面只承诺
|
|
10
|
-
* content 六类,新 core 帧类型先经这里显式收编再出 wire)。
|
|
10
|
+
* content 六类,新 core 帧类型先经这里显式收编再出 wire)。
|
|
11
|
+
* ⚠️ **本函数是无状态逐帧投影**:`reasoning_end`(S-310 的推理权威段)不在这里 —— 它要按句柄攒段,
|
|
12
|
+
* 住在 {@link SubagentTailBus.onForwardFrame}(publish 侧是唯一知道句柄的地方)。 */
|
|
11
13
|
export declare function projectTailFrame(e: Record<string, unknown> & {
|
|
12
14
|
type?: string;
|
|
13
15
|
}): SubagentTailFrame | null;
|
|
@@ -50,9 +52,33 @@ export declare class SubagentTailBus {
|
|
|
50
52
|
* 不需要"先有内容帧"才能学到"没有内容帧"。
|
|
51
53
|
*/
|
|
52
54
|
private readonly modeByHandle;
|
|
55
|
+
/**
|
|
56
|
+
* 句柄 → 该句柄这条 tail 流上**在攒的推理段**(S-310)。
|
|
57
|
+
*
|
|
58
|
+
* 键必须是句柄,理由与 {@link modeByHandle} 同源再加一条硬的:两个并发子代的 `reasoning_delta` 在
|
|
59
|
+
* **同一个** forward sink 上交错到达,一只累积器会把两份思考拼成一段、再盖上其中一个的身份键。
|
|
60
|
+
*
|
|
61
|
+
* 生命期 = **有人在听的那段时间**:攒段住在 `hasSubscribers` 快门之内(tail 是 live-only 面,没人听
|
|
62
|
+
* 就不投影也不攒),最后一个订阅者走掉或句柄终态({@link forgetHandle})时整条删掉 ⇒ 本表恒被
|
|
63
|
+
* 订阅者数量所界,不需要第二道容量闸。
|
|
64
|
+
*/
|
|
65
|
+
private readonly segByHandle;
|
|
53
66
|
/** 三条 run 腿的 forward 回调里逐帧调(在 `hasSubscribers` 快门**之前**——没人在听时也要学,
|
|
54
67
|
* 否则晚开的 tail 永远读不到供给形)。同值重复写是 no-op(热路径只付一次 Map.get)。 */
|
|
55
68
|
noteHandleContentMode(bgAgentId: string, mode: TailContentMode): void;
|
|
69
|
+
/**
|
|
70
|
+
* 🔴 **三条 run 腿的 forward 回调唯一入口**(S-310 收口)——登记供给形 → 段界收段 → 逐帧投影 → publish。
|
|
71
|
+
*
|
|
72
|
+
* 此前这四步以**同一段六行代码**抄在三条腿上(sync 流腿、verify-resume 腿、resume-stream 腿),
|
|
73
|
+
* 差别为零;S-310 要给 tail 面补 `reasoning_end` 时,那就是第四、第五、第六份拷贝。收成一扇门之后
|
|
74
|
+
* 腿上只剩「句柄在不在」这一句判断,三腿不可能再分家(`test/subagent-tail-stream.test.ts` G18-G20)。
|
|
75
|
+
*
|
|
76
|
+
* 顺序即语义:权威段**先于**触发它的结构帧上 wire(消费端「整段替换已攒 delta」的替换点不与卡片交错),
|
|
77
|
+
* 与账本腿「文本行先于结构行」逐字同构。
|
|
78
|
+
*/
|
|
79
|
+
onForwardFrame(bgAgentId: string, mode: TailContentMode, e: Record<string, unknown> & {
|
|
80
|
+
type?: string;
|
|
81
|
+
}): void;
|
|
56
82
|
/**
|
|
57
83
|
* 句柄终态时**撤销**登记(三条腿的 `onTaskNotification` 观察者调,与 `task_settled` 帧同处)。
|
|
58
84
|
*
|
|
@@ -69,7 +95,7 @@ export declare class SubagentTailBus {
|
|
|
69
95
|
* 的答案。core 今天把终态通知排在最后一条 forward 之后(fleet-bus 同注),复活又是人手动作,这条窗
|
|
70
96
|
* 实际上不可达;真要关死需要给句柄加墓碑态,代价大于一位诊断标签的收益,故不做。
|
|
71
97
|
*/
|
|
72
|
-
|
|
98
|
+
forgetHandle(bgAgentId: string): void;
|
|
73
99
|
/** 路由读(按 tail 的 target 句柄)。缺席 = 不知道,路由出 `"unknown"`。 */
|
|
74
100
|
handleContentMode(bgAgentId: string): TailContentMode | undefined;
|
|
75
101
|
/** 两条 run 腿的 fwdInternals 调用点:帧已投影(projectTailFrame),null 不进来。 */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { redactSecrets } from "../trace/redact.js";
|
|
2
2
|
import { taskProgressEventData, toolStartEventData, toolEndEventData, textEndEventData } from "../trace/project.js";
|
|
3
|
+
import { createReasoningSegments } from "../trace/model-segment.js";
|
|
3
4
|
const TAIL_QUEUE_CAP = 512;
|
|
4
5
|
export function projectTailFrame(e) {
|
|
5
6
|
const t = e.type;
|
|
@@ -41,12 +42,31 @@ const HOST_MODE_CAP = 4096;
|
|
|
41
42
|
export class SubagentTailBus {
|
|
42
43
|
subs = new Map();
|
|
43
44
|
modeByHandle = new Map();
|
|
45
|
+
segByHandle = new Map();
|
|
44
46
|
noteHandleContentMode(bgAgentId, mode) {
|
|
45
47
|
if (this.modeByHandle.get(bgAgentId) === mode)
|
|
46
48
|
return;
|
|
47
49
|
putBounded(this.modeByHandle, bgAgentId, mode);
|
|
48
50
|
}
|
|
49
|
-
|
|
51
|
+
onForwardFrame(bgAgentId, mode, e) {
|
|
52
|
+
this.noteHandleContentMode(bgAgentId, mode);
|
|
53
|
+
if (!this.hasSubscribers(bgAgentId))
|
|
54
|
+
return;
|
|
55
|
+
let segs = this.segByHandle.get(bgAgentId);
|
|
56
|
+
if (segs === undefined) {
|
|
57
|
+
segs = createReasoningSegments();
|
|
58
|
+
this.segByHandle.set(bgAgentId, segs);
|
|
59
|
+
}
|
|
60
|
+
for (const end of segs.onEvent(e))
|
|
61
|
+
this.publish(bgAgentId, end);
|
|
62
|
+
const f = projectTailFrame(e);
|
|
63
|
+
if (f)
|
|
64
|
+
this.publish(bgAgentId, f);
|
|
65
|
+
}
|
|
66
|
+
forgetHandle(bgAgentId) {
|
|
67
|
+
for (const end of this.segByHandle.get(bgAgentId)?.cut() ?? [])
|
|
68
|
+
this.publish(bgAgentId, end);
|
|
69
|
+
this.segByHandle.delete(bgAgentId);
|
|
50
70
|
this.modeByHandle.delete(bgAgentId);
|
|
51
71
|
}
|
|
52
72
|
handleContentMode(bgAgentId) {
|
|
@@ -84,8 +104,10 @@ export class SubagentTailBus {
|
|
|
84
104
|
sub.closed = true;
|
|
85
105
|
sub.wake?.();
|
|
86
106
|
set.delete(sub);
|
|
87
|
-
if (set.size === 0 && this.subs.get(bgAgentId) === set)
|
|
107
|
+
if (set.size === 0 && this.subs.get(bgAgentId) === set) {
|
|
88
108
|
this.subs.delete(bgAgentId);
|
|
109
|
+
this.segByHandle.delete(bgAgentId);
|
|
110
|
+
}
|
|
89
111
|
};
|
|
90
112
|
const it = {
|
|
91
113
|
[Symbol.asyncIterator]() {
|
|
@@ -44,6 +44,25 @@ export declare function principalTokenPresentedButUnverified(req: IncomingMessag
|
|
|
44
44
|
* 调用链上的**证明三件套**,与本门(身份面)是两道门。
|
|
45
45
|
*/
|
|
46
46
|
export declare function sendPrincipalRefusal(res: ServerResponse, req: IncomingMessage, config: ServiceConfig): void;
|
|
47
|
+
/**
|
|
48
|
+
* 🔴 **「这个调用方读得到这一行 run 吗」的唯一谓词**(S-297 抽取;此前只活在 `server.ts runOwnerOk` 的
|
|
49
|
+
* 函数体里,而 `runOwnerOk` 把**判断**与**404 怎么发**焊在一起 —— 于是任何需要同一条判断但不想 404 的
|
|
50
|
+
* 读面(如 `/mcp` 面板的 `lastLegMcp`:非属主就不投这个键、面板照常 200)只能手抄一份,而手抄那份必然
|
|
51
|
+
* 漏掉下面这条窄互认臂:`explicitOperatorOk` 顶注已经为同族的病写过「SINGLE mint point,不要再造兄弟」)。
|
|
52
|
+
*
|
|
53
|
+
* 三条放行臂,别的一律不放:
|
|
54
|
+
* · `owner === null` —— 无属主的行(dev / 存量),本来就是开放的;
|
|
55
|
+
* · `principal === owner` —— 自己的行。⚠️ `principal` 必须是**验过**的身份(`gatedPrincipal`),
|
|
56
|
+
* 不是自述头 —— `owner` 是创建时从验过的 principal 落库的,拿自述头来比就等于谁都能读别人的 run;
|
|
57
|
+
* · [ref] C8 **窄互认**:非 `requirePrincipal` 部署上那批 `anon:shell-live` 哨兵行 + 无身份的调用方
|
|
58
|
+
* (cli 停发哨兵头后的存量会话,否则「能续跑但看不见」)。`gatedPrincipal` 的回值是 `string | undefined`
|
|
59
|
+
* ⇒ 原文那半个 `principal === null` 比较结构上不可达,抽取时一并去掉(语义逐字不变)。
|
|
60
|
+
*
|
|
61
|
+
* **不含 operator 臂**(刻意,[ref]⑥ 台账):跨属主 operator 在 poll/events/cancel 三读面上今天就是 404,
|
|
62
|
+
* 放宽它是一次独立裁定。需要 operator 臂的读面在**自己的调用点**显式并上 {@link explicitOperatorOk} ——
|
|
63
|
+
* 那样「谁多给了 operator 一条路」在代码上看得见,而不是被偷偷塞进共享谓词里。
|
|
64
|
+
*/
|
|
65
|
+
export declare function runOwnerReadable(principal: string | undefined, owner: string | null, config: ServiceConfig): boolean;
|
|
47
66
|
/** The EXPLICIT operator gate (IMAGE-API-DESIGN.md §P2.4a MUST-FIX) — NOT the bare `isOperator` (whose empty-list
|
|
48
67
|
* "true-for-all" is an unrestricted-write exec door the instant OPERATOR_PRINCIPALS is empty). Requires a non-empty
|
|
49
68
|
* operator set AND a present principal IN it. The same form the preempt/resume endpoints already use.
|
|
@@ -65,6 +65,13 @@ export function sendPrincipalRefusal(res, req, config) {
|
|
|
65
65
|
}
|
|
66
66
|
sendError(res, 401, "auth.principal_required", `missing principal header '${config.principalHeader}'`);
|
|
67
67
|
}
|
|
68
|
+
export function runOwnerReadable(principal, owner, config) {
|
|
69
|
+
if (owner === null)
|
|
70
|
+
return true;
|
|
71
|
+
if (principal === owner)
|
|
72
|
+
return true;
|
|
73
|
+
return !config.requirePrincipal && owner === "anon:shell-live" && principal === undefined;
|
|
74
|
+
}
|
|
68
75
|
export function explicitOperatorOk(principal, operatorPrincipals) {
|
|
69
76
|
return operatorPrincipals.length > 0 && principal !== undefined && operatorPrincipals.includes(principal);
|
|
70
77
|
}
|
|
@@ -3,14 +3,16 @@ import { a2aForScenario, mcpForScenario } from "../../config-center/facade.js";
|
|
|
3
3
|
import { isUuidV7, isUuidShape, legacyUnownedSessionOk } from "../../security.js";
|
|
4
4
|
import { windowMessages, truncateMessageBlobs } from "../../audit.js";
|
|
5
5
|
import { redactSecrets } from "../../trace/redact.js";
|
|
6
|
+
import { recordFailOpen } from "../../observability/fail-open.js";
|
|
6
7
|
import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_PERMISSION_RULES, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS } from "../../task-settings.js";
|
|
7
8
|
import { sendJson, sendError, sseHeaders } from "../send.js";
|
|
8
|
-
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
9
|
+
import { gatedPrincipal, explicitOperatorOk, runOwnerReadable, sendPrincipalRefusal } from "../principal-gate.js";
|
|
9
10
|
const MCP_STATUS_TTL_MS = 5_000;
|
|
10
11
|
const MCP_MATERIALIZE_TIMEOUT_MS = 10_000;
|
|
11
12
|
const A2A_STATUS_TTL_MS = 5_000;
|
|
12
13
|
const A2A_MATERIALIZE_TIMEOUT_MS = 10_000;
|
|
13
14
|
const MEMORY_STATUS_TTL_MS = 5_000;
|
|
15
|
+
const LAST_LEG_LEDGER_TIMEOUT_MS = 2_000;
|
|
14
16
|
const A2A_PANEL_MAX_TOOL_NAMES = 200;
|
|
15
17
|
export function createSessionsLocal() {
|
|
16
18
|
return {
|
|
@@ -21,6 +23,44 @@ export function createSessionsLocal() {
|
|
|
21
23
|
memoryStatusCache: new Map(),
|
|
22
24
|
};
|
|
23
25
|
}
|
|
26
|
+
async function lastLegMcpFor(deps, sessionId, principal) {
|
|
27
|
+
let timer;
|
|
28
|
+
try {
|
|
29
|
+
return await Promise.race([
|
|
30
|
+
readLastLegMcp(deps, sessionId, principal),
|
|
31
|
+
new Promise((r) => { timer = setTimeout(() => { recordFailOpen("server.mcp-panel.last-leg-unreadable", `ledger read exceeded ${LAST_LEG_LEDGER_TIMEOUT_MS}ms`); r(undefined); }, LAST_LEG_LEDGER_TIMEOUT_MS); }),
|
|
32
|
+
]);
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
recordFailOpen("server.mcp-panel.last-leg-unreadable", e instanceof Error ? e.message : String(e ?? ""));
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
if (timer)
|
|
40
|
+
clearTimeout(timer);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async function readLastLegMcp(deps, sessionId, principal) {
|
|
44
|
+
const runStore = deps.runStore;
|
|
45
|
+
if (!runStore || sessionId === null)
|
|
46
|
+
return undefined;
|
|
47
|
+
const latest = await runStore.latestRunForSession(sessionId);
|
|
48
|
+
if (latest === undefined)
|
|
49
|
+
return undefined;
|
|
50
|
+
if (!runOwnerReadable(principal, latest.owner, deps.config) && !explicitOperatorOk(principal, deps.config.operatorPrincipals))
|
|
51
|
+
return undefined;
|
|
52
|
+
const events = await runStore.getEvents(latest.taskId, 0);
|
|
53
|
+
for (let i = events.length - 1; i >= 0; i--) {
|
|
54
|
+
const ev = events[i];
|
|
55
|
+
if (ev.type !== "wiring_manifest")
|
|
56
|
+
continue;
|
|
57
|
+
const mcp = ev.data?.mcp;
|
|
58
|
+
if (!Array.isArray(mcp))
|
|
59
|
+
return undefined;
|
|
60
|
+
return { runId: latest.taskId, at: ev.ts, mcp };
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
24
64
|
export async function handleSessions(req, res, url, ctx) {
|
|
25
65
|
const miss = { fell: false };
|
|
26
66
|
await handleSessionsBody(req, res, url, ctx, miss);
|
|
@@ -71,17 +111,18 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
|
71
111
|
{
|
|
72
112
|
const mm = /^\/v1\/sessions\/([^/]+)\/mcp$/.exec(url);
|
|
73
113
|
if (mm && req.method === "GET") {
|
|
74
|
-
|
|
114
|
+
const principal = gatedPrincipal(req, deps.config);
|
|
115
|
+
if (deps.config.requirePrincipal && !principal) {
|
|
75
116
|
sendPrincipalRefusal(res, req, deps.config);
|
|
76
117
|
return;
|
|
77
118
|
}
|
|
119
|
+
const lastLegMcp = await lastLegMcpFor(deps, safeDecode(mm[1]), principal);
|
|
78
120
|
const specs = mcpForScenario(deps.config.mcpServers, deps.config.defaultScenario);
|
|
121
|
+
const cacheKey = `${deps.config.defaultScenario}::${principal ?? ""}`;
|
|
79
122
|
if (!specs || specs.length === 0) {
|
|
80
|
-
sendJson(res, 200, { servers: [], asOf: new Date().toISOString() });
|
|
123
|
+
sendJson(res, 200, { servers: [], asOf: new Date().toISOString(), ...(lastLegMcp === undefined ? {} : { lastLegMcp }) });
|
|
81
124
|
return;
|
|
82
125
|
}
|
|
83
|
-
const principal = gatedPrincipal(req, deps.config);
|
|
84
|
-
const cacheKey = `${deps.config.defaultScenario}::${principal ?? ""}`;
|
|
85
126
|
const cached = mcpStatusCache.get(cacheKey);
|
|
86
127
|
if (!cached || Date.now() - cached.at > MCP_STATUS_TTL_MS) {
|
|
87
128
|
if (mcpStatusCache.size > 1024)
|
|
@@ -114,7 +155,7 @@ async function handleSessionsBody(req, res, url, ctx, miss) {
|
|
|
114
155
|
})();
|
|
115
156
|
mcpStatusCache.set(cacheKey, { at: Date.now(), panel });
|
|
116
157
|
}
|
|
117
|
-
sendJson(res, 200, await mcpStatusCache.get(cacheKey).panel);
|
|
158
|
+
sendJson(res, 200, { ...(await mcpStatusCache.get(cacheKey).panel), ...(lastLegMcp === undefined ? {} : { lastLegMcp }) });
|
|
118
159
|
return;
|
|
119
160
|
}
|
|
120
161
|
}
|
|
@@ -4,7 +4,8 @@ import { graftParkToolCallId, markChildrenStoppedByUserOnAbort, resumeAtHttpStat
|
|
|
4
4
|
import { stripResumeToken } from "../../terminal.js";
|
|
5
5
|
import { withPrincipal } from "../../observability/principal-context.js";
|
|
6
6
|
import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals, isFleetAgentTerminalNotification } from "../../fleet/fleet-bus.js";
|
|
7
|
-
import { defaultSubagentTailBus
|
|
7
|
+
import { defaultSubagentTailBus } from "../../fleet/subagent-tail-bus.js";
|
|
8
|
+
import { createReasoningSegments, reasoningStreamOfTask } from "../../trace/model-segment.js";
|
|
8
9
|
import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKey, NotifiedKeys } from "../../orchestration/workflow-completion-inbox.js";
|
|
9
10
|
import { createLedgerSink } from "../../trace/ledger-sink.js";
|
|
10
11
|
import { registerEngineNoticeLeg } from "../../trace/engine-notice-wire.js";
|
|
@@ -12,7 +13,7 @@ import { clearTurnActivity, readTurnActivityMs, recordTurnActivity } from "../..
|
|
|
12
13
|
import { mintCancelledResult } from "../../run-cancel-context.js";
|
|
13
14
|
import { recordResourceWindow } from "../../resource-window.js";
|
|
14
15
|
import { redactSecrets } from "../../trace/redact.js";
|
|
15
|
-
import { contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, wiringManifestOperatorEventData, toolRosterDeltaEventData, humanInputEventData, appendModelUsageDelta, attachModelUsage, textEndEventData } from "../../trace/project.js";
|
|
16
|
+
import { contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, wiringManifestOperatorEventData, toolRosterDeltaEventData, toolProgressEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, attachModelUsage, textEndEventData } from "../../trace/project.js";
|
|
16
17
|
import { cascadeConfig, runMeta } from "../run-meta.js";
|
|
17
18
|
import { scopedIdempotencyKey } from "../idempotency.js";
|
|
18
19
|
import { sendJson, sendError, sseHeaders } from "../send.js";
|
|
@@ -313,23 +314,21 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
313
314
|
const streamBody = async () => {
|
|
314
315
|
await withPrincipal(principal, async () => {
|
|
315
316
|
const tailContentMode = prepared.spec.forwardSubagentEvents === true ? "on" : "progress_only";
|
|
317
|
+
const childReasoning = createReasoningSegments();
|
|
318
|
+
const topReasoning = createReasoningSegments();
|
|
316
319
|
const fwdInternals = {
|
|
317
320
|
onForwardEvent: (e) => {
|
|
318
321
|
fleetPub?.onForwardEvent(e);
|
|
319
322
|
ledgerSink?.onForwardEvent(e);
|
|
320
323
|
{
|
|
321
324
|
const bg = e.bgAgentId;
|
|
322
|
-
if (bg !== undefined)
|
|
323
|
-
defaultSubagentTailBus.
|
|
324
|
-
if (defaultSubagentTailBus.hasSubscribers(bg)) {
|
|
325
|
-
const f = projectTailFrame(e);
|
|
326
|
-
if (f)
|
|
327
|
-
defaultSubagentTailBus.publish(bg, f);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
325
|
+
if (bg !== undefined)
|
|
326
|
+
defaultSubagentTailBus.onForwardFrame(bg, tailContentMode, e);
|
|
330
327
|
}
|
|
331
328
|
if (res.writableEnded)
|
|
332
329
|
return;
|
|
330
|
+
for (const end of childReasoning.onEvent(e))
|
|
331
|
+
sseData(res, end);
|
|
333
332
|
const t = e.type;
|
|
334
333
|
if (t === "task_progress") {
|
|
335
334
|
sseData(res, { type: "task_progress", ...taskProgressEventData(e) });
|
|
@@ -359,7 +358,10 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
359
358
|
onTaskNotification: (n, opts) => {
|
|
360
359
|
if (n.task_type === "workflow")
|
|
361
360
|
return;
|
|
362
|
-
|
|
361
|
+
if (!res.writableEnded)
|
|
362
|
+
for (const end of childReasoning.cut(reasoningStreamOfTask(n.task_id, n.sessionId)))
|
|
363
|
+
sseData(res, end);
|
|
364
|
+
defaultSubagentTailBus.forgetHandle(n.task_id);
|
|
363
365
|
if (defaultSubagentTailBus.hasSubscribers(n.task_id)) {
|
|
364
366
|
defaultSubagentTailBus.publish(n.task_id, { type: "task_settled", taskId: n.task_id, status: n.status, ...(typeof n.seq === "number" ? { seq: n.seq } : {}), ...(n.summary ? { summary: redactSecrets(n.summary) } : {}) });
|
|
365
367
|
}
|
|
@@ -393,6 +395,8 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
393
395
|
for await (const ev of liveStream) {
|
|
394
396
|
if (closed)
|
|
395
397
|
break;
|
|
398
|
+
for (const end of ev.type === "done" ? [...childReasoning.cut(), ...topReasoning.cut()] : topReasoning.onEvent(ev))
|
|
399
|
+
sseData(res, end);
|
|
396
400
|
if (ev.type === "message_committed" && ev.role === "user")
|
|
397
401
|
userMsgEntryId = ev.entryId;
|
|
398
402
|
const cancelVerbAtDone = ev.type === "done" && durableTaskId !== undefined && cancelledViaVerb.has(durableTaskId);
|
|
@@ -483,6 +487,16 @@ async function handleTasksBody(req, res, url, ctx, miss) {
|
|
|
483
487
|
if (rosterDelta !== undefined)
|
|
484
488
|
sseData(res, { type: "tool_roster_delta", ...rosterDelta });
|
|
485
489
|
}
|
|
490
|
+
else if (ev.type === "tool_progress") {
|
|
491
|
+
const progress = toolProgressEventData(ev);
|
|
492
|
+
if (progress !== undefined)
|
|
493
|
+
sseData(res, { type: "tool_progress", ...progress });
|
|
494
|
+
}
|
|
495
|
+
else if (ev.type === "tool_disclosure") {
|
|
496
|
+
const disclosure = toolDisclosureEventData(ev);
|
|
497
|
+
if (disclosure !== undefined)
|
|
498
|
+
sseData(res, { type: "tool_disclosure", ...disclosure });
|
|
499
|
+
}
|
|
486
500
|
else if (ev.type === "human_input") {
|
|
487
501
|
sseData(res, { type: "human_input", ...humanInputEventData(ev) });
|
|
488
502
|
}
|
|
@@ -246,7 +246,12 @@ function projectLegApprovalKeys(agent) {
|
|
|
246
246
|
};
|
|
247
247
|
}
|
|
248
248
|
function projectParkRow(p) {
|
|
249
|
-
return {
|
|
249
|
+
return {
|
|
250
|
+
callKey: p.callKey,
|
|
251
|
+
sessionId: p.sessionId,
|
|
252
|
+
originRunId: p.originRunId,
|
|
253
|
+
...(p.originUnconfirmed === true ? { originUnconfirmed: true } : {}),
|
|
254
|
+
};
|
|
250
255
|
}
|
|
251
256
|
function summarizeWorkflowDetail(run) {
|
|
252
257
|
const isDone = (s) => s === "completed" || s === "failed";
|
|
@@ -343,6 +348,7 @@ function summarizeWorkflowDetail(run) {
|
|
|
343
348
|
...(run.error !== undefined ? { error: redactErrorMessage(run.error) ?? "" } : {}),
|
|
344
349
|
...(run.errorCode !== undefined ? { errorCode: run.errorCode } : {}),
|
|
345
350
|
...(run.parks !== undefined ? { parks: run.parks.map(projectParkRow) } : {}),
|
|
351
|
+
...(run.resumeAdmissionIncomplete === true ? { resumeAdmissionIncomplete: true } : {}),
|
|
346
352
|
...(run.result !== undefined ? { result: redactSecrets(run.result) } : {}),
|
|
347
353
|
phases,
|
|
348
354
|
...(unphasedView ? { unphased: unphasedView } : {}),
|
package/dist/http/server.js
CHANGED
|
@@ -26,14 +26,14 @@ import { looksLikeJwt } from "@sema-agent/settings-schema/api/auth-bridge";
|
|
|
26
26
|
import {} from "../orchestration/workflow-agent-steer.js";
|
|
27
27
|
import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKey, NotifiedKeys } from "../orchestration/workflow-completion-inbox.js";
|
|
28
28
|
import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals, isFleetAgentTerminalNotification } from "../fleet/fleet-bus.js";
|
|
29
|
-
import { defaultSubagentTailBus
|
|
29
|
+
import { defaultSubagentTailBus } from "../fleet/subagent-tail-bus.js";
|
|
30
30
|
import { APPROVAL_GATE_KINDS, createApprovalCardEmitter, isApprovalGateKind, resolveApprovalLeg, resolveStreamApprovalGate } from "../tool-approval.js";
|
|
31
31
|
import { buildCheckpointAnchorHash } from "../approval-ask-audit-store.js";
|
|
32
32
|
import { TERMINAL_PARK_GATE_KINDS } from "../plugins/checkpoint-store-sql.js";
|
|
33
33
|
import { createWallClockJumpGuard } from "../wall-clock-jump-guard.js";
|
|
34
34
|
import {} from "../observability/rate-limit.js";
|
|
35
35
|
import { withPrincipal } from "../observability/principal-context.js";
|
|
36
|
-
import { modelTextEventData, turnEndEventData, contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, humanInputEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "../trace/project.js";
|
|
36
|
+
import { modelTextEventData, turnEndEventData, contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "../trace/project.js";
|
|
37
37
|
import { redactSecrets } from "../trace/redact.js";
|
|
38
38
|
import { createLedgerSink, createSerialLedgerAppend } from "../trace/ledger-sink.js";
|
|
39
39
|
import { registerEngineNoticeLeg } from "../trace/engine-notice-wire.js";
|
|
@@ -85,7 +85,7 @@ export { cascadeConfig };
|
|
|
85
85
|
import { handleImages, createImagesLocal, coarseStatusForState, errorCodeForExit } from "./routes/images.js";
|
|
86
86
|
export { coarseStatusForState, errorCodeForExit };
|
|
87
87
|
import { sendJson, sendError, httpErrorCode, msg, isSseResponse } from "./send.js";
|
|
88
|
-
import { authorized, systemFor, gatedPrincipal, explicitOperatorOk, isOperator, sendPrincipalRefusal } from "./principal-gate.js";
|
|
88
|
+
import { authorized, systemFor, gatedPrincipal, explicitOperatorOk, isOperator, runOwnerReadable, sendPrincipalRefusal } from "./principal-gate.js";
|
|
89
89
|
import { buildActiveRunConflict, resumeEntryForGate } from "./active-run-conflict.js";
|
|
90
90
|
import { ASK_USER_QUESTION_TOOL_NAME } from "../approval-content-kind.js";
|
|
91
91
|
export { explicitOperatorOk, isOperator };
|
|
@@ -1453,14 +1453,8 @@ export function createHttpServer(rawDeps) {
|
|
|
1453
1453
|
fleetPub?.onForwardEvent(e);
|
|
1454
1454
|
{
|
|
1455
1455
|
const bg = e.bgAgentId;
|
|
1456
|
-
if (bg !== undefined)
|
|
1457
|
-
defaultSubagentTailBus.
|
|
1458
|
-
if (defaultSubagentTailBus.hasSubscribers(bg)) {
|
|
1459
|
-
const f = projectTailFrame(e);
|
|
1460
|
-
if (f)
|
|
1461
|
-
defaultSubagentTailBus.publish(bg, f);
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1456
|
+
if (bg !== undefined)
|
|
1457
|
+
defaultSubagentTailBus.onForwardFrame(bg, verifyTailContentMode, e);
|
|
1464
1458
|
}
|
|
1465
1459
|
if (rsV && taskId)
|
|
1466
1460
|
verifySink.onForwardEvent(e);
|
|
@@ -1474,7 +1468,7 @@ export function createHttpServer(rawDeps) {
|
|
|
1474
1468
|
onTaskNotification: (n, opts) => {
|
|
1475
1469
|
if (n.task_type === "workflow")
|
|
1476
1470
|
return;
|
|
1477
|
-
defaultSubagentTailBus.
|
|
1471
|
+
defaultSubagentTailBus.forgetHandle(n.task_id);
|
|
1478
1472
|
if (defaultSubagentTailBus.hasSubscribers(n.task_id)) {
|
|
1479
1473
|
defaultSubagentTailBus.publish(n.task_id, { type: "task_settled", taskId: n.task_id, status: n.status, ...(typeof n.seq === "number" ? { seq: n.seq } : {}), ...(n.summary ? { summary: redactSecrets(n.summary) } : {}) });
|
|
1480
1474
|
}
|
|
@@ -1578,14 +1572,8 @@ export function createHttpServer(rawDeps) {
|
|
|
1578
1572
|
fleetPub?.onForwardEvent(e);
|
|
1579
1573
|
{
|
|
1580
1574
|
const bg = e.bgAgentId;
|
|
1581
|
-
if (bg !== undefined)
|
|
1582
|
-
defaultSubagentTailBus.
|
|
1583
|
-
if (defaultSubagentTailBus.hasSubscribers(bg)) {
|
|
1584
|
-
const f = projectTailFrame(e);
|
|
1585
|
-
if (f)
|
|
1586
|
-
defaultSubagentTailBus.publish(bg, f);
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1575
|
+
if (bg !== undefined)
|
|
1576
|
+
defaultSubagentTailBus.onForwardFrame(bg, tailContentMode, e);
|
|
1589
1577
|
}
|
|
1590
1578
|
if (!rs || !taskId)
|
|
1591
1579
|
return;
|
|
@@ -1604,7 +1592,7 @@ export function createHttpServer(rawDeps) {
|
|
|
1604
1592
|
onTaskNotification: (n, opts) => {
|
|
1605
1593
|
if (n.task_type === "workflow")
|
|
1606
1594
|
return;
|
|
1607
|
-
defaultSubagentTailBus.
|
|
1595
|
+
defaultSubagentTailBus.forgetHandle(n.task_id);
|
|
1608
1596
|
if (defaultSubagentTailBus.hasSubscribers(n.task_id)) {
|
|
1609
1597
|
defaultSubagentTailBus.publish(n.task_id, { type: "task_settled", taskId: n.task_id, status: n.status, ...(typeof n.seq === "number" ? { seq: n.seq } : {}), ...(n.summary ? { summary: redactSecrets(n.summary) } : {}) });
|
|
1610
1598
|
}
|
|
@@ -1699,6 +1687,13 @@ export function createHttpServer(rawDeps) {
|
|
|
1699
1687
|
await append("tool_roster_delta", rosterDelta);
|
|
1700
1688
|
break;
|
|
1701
1689
|
}
|
|
1690
|
+
case "tool_disclosure": {
|
|
1691
|
+
await flush();
|
|
1692
|
+
const disclosure = toolDisclosureEventData(ev);
|
|
1693
|
+
if (disclosure !== undefined)
|
|
1694
|
+
await append("tool_disclosure", disclosure);
|
|
1695
|
+
break;
|
|
1696
|
+
}
|
|
1702
1697
|
case "human_input":
|
|
1703
1698
|
await flush();
|
|
1704
1699
|
await append("human_input", humanInputEventData(ev));
|
|
@@ -2334,8 +2329,7 @@ export function createHttpServer(rawDeps) {
|
|
|
2334
2329
|
sendPrincipalRefusal(res, req, deps.config);
|
|
2335
2330
|
return false;
|
|
2336
2331
|
}
|
|
2337
|
-
|
|
2338
|
-
if (!legacyShellOwner && owner !== null && principal !== owner) {
|
|
2332
|
+
if (!runOwnerReadable(principal, owner, deps.config)) {
|
|
2339
2333
|
if (notFoundForm === "session")
|
|
2340
2334
|
sendError(res, 404, "not_found.session", "session not found");
|
|
2341
2335
|
else
|
|
@@ -2531,6 +2525,7 @@ const WORKER_SWAP_REDEEMABLE = {
|
|
|
2531
2525
|
real_approval_forged: false,
|
|
2532
2526
|
read_face_damaged: false,
|
|
2533
2527
|
read_face_forged: false,
|
|
2528
|
+
write_face_damaged: false,
|
|
2534
2529
|
constraint_chain_missing: false,
|
|
2535
2530
|
};
|
|
2536
2531
|
function checkpointRowRedeemableElsewhere(reason) {
|