@sema-agent/client-core 0.73.3 → 0.73.5

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.
@@ -0,0 +1,155 @@
1
+ /**
2
+ * src/fleet/workflowSizeWarning.ts —— workflow **规模告警**的判定(0.73.5)。
3
+ *
4
+ * 三端 footer / 面板都要回答同一个问题:「这条 workflow 是不是大到该提醒用户了」。判定与 CC 同形
5
+ * (三个缺省帽、每 agent 用量估算、guideline 表、两个 env 覆盖、三档文案与宽度阶梯),属于客户端公共逻辑,
6
+ * 由本包单源;端只负责读环境、读设置、量宽度与渲染。
7
+ *
8
+ * 🔴 **入参按 fleet 视图的三态键读**(`FleetWorkflowView`:`totalCount` / `startedCount` / `doneCount` /
9
+ * `failedCount` / `tokens` 全是可选位,缺席 = wire 没报 = 不知道)。出参三态:
10
+ * · `warn` —— 有**正向证据**证明至少一条轴超帽(别的键缺席不挡它;下界分母算出的上界**不算**证据);
11
+ * · `ok` —— 两条轴都判得出,且都在帽内(或 ultracode 压掉了整条告警);
12
+ * · `unknown` —— 没有超帽的正向证据,且至少一条轴判不出。**未知的规模不能被当作规模正常**:
13
+ * `unknown` 绝不折成 `ok`,`missing` 点名缺了哪几键。
14
+ *
15
+ * 🔴 **分母口径**(每 agent 用量 `u = tokens / started`):
16
+ * · `startedCount` 在场且不小于 `doneCount + failedCount` ⇒ 用引擎真值(含真 0:「一个都还没起」是一条真信息
17
+ * ⇒ 用先验);比已完成数还小(帧间竞态)⇒ 这份读数是陈旧的,按下面的下界处置;
18
+ * · 缺席 ⇒ 只有 `doneCount` / `failedCount` **两键都在场**才回退成下界,结果标 `startedIsLowerBound`。
19
+ * 下界偏小 ⇒ `u` 偏大 ⇒ 算出来的是投影的**上界**(`projectedTokensUpperBound`,不是 `projectedTokens`):
20
+ * 上界在帽内 ⇒ 真的在帽内 ⇒ 可以判 `ok`;上界超帽**不是**超帽的证据(同一份读数补上真 `startedCount`
21
+ * 可能远在帽内)⇒ 投影轴判不出,`unknown`。想保守一点的端可以自己拿上界去提示,判词不替它撒谎;
22
+ * · 下界为 0 ⇒ 每 agent 用量判不出:「0 个完成」不是「一个没起」的证据,先验只在引擎**亲口**报
23
+ * `startedCount = 0` 时可用;
24
+ * · 只有一键在场 ⇒ 不回退(缺席的那键不读作 0)。
25
+ */
26
+ /** 缺省 agent 数帽。 */
27
+ export const WORKFLOW_SIZE_WARNING_AGENT_CAP = 25;
28
+ /** 缺省 token 帽(已花 ∨ 投影)。 */
29
+ export const WORKFLOW_SIZE_WARNING_TOKEN_CAP = 1_500_000;
30
+ /** 还没有任何 agent 起跑时的每 agent 用量先验。 */
31
+ export const WORKFLOW_SIZE_WARNING_TOKENS_PER_AGENT = 70_000;
32
+ /** 显式 `workflowSizeGuideline` → agent 数帽。`unrestricted` 与缺省(未配置)**不贡献帽**,故不在表里。 */
33
+ export const WORKFLOW_SIZE_GUIDELINE_AGENT_CAPS = Object.freeze({
34
+ small: 5,
35
+ medium: 15,
36
+ large: 50,
37
+ });
38
+ /** 两个 env 覆盖各自的名字,按读取优先级排(品牌名在前,兼容名在后)。本包不读 `process.env`,名字单源在这里。 */
39
+ export const WORKFLOW_SIZE_WARNING_ENV_NAMES = Object.freeze({
40
+ agents: Object.freeze(['SEMA_CODE_WORKFLOW_SIZE_WARNING_AGENTS', 'CLAUDE_CODE_WORKFLOW_SIZE_WARNING_AGENTS']),
41
+ tokens: Object.freeze(['SEMA_CODE_WORKFLOW_SIZE_WARNING_TOKENS', 'CLAUDE_CODE_WORKFLOW_SIZE_WARNING_TOKENS']),
42
+ });
43
+ /** 三档文案(宽 → 窄)。 */
44
+ export const WORKFLOW_SIZE_WARNING_TEXT_LONG = '⚠ Large workflow · /workflows to stop';
45
+ export const WORKFLOW_SIZE_WARNING_TEXT_SHORT = '⚠ Large workflow';
46
+ export const WORKFLOW_SIZE_WARNING_TEXT_MIN = '⚠';
47
+ /** 有限非负数,否则缺席(坏形按缺席,不猜)。 */
48
+ function count(v) {
49
+ return typeof v === 'number' && Number.isFinite(v) && v >= 0 ? v : undefined;
50
+ }
51
+ /** 正有限数,否则缺席。 */
52
+ function positive(v) {
53
+ return typeof v === 'number' && Number.isFinite(v) && v > 0 ? v : undefined;
54
+ }
55
+ function guidelineCap(size) {
56
+ return size !== undefined && Object.hasOwn(WORKFLOW_SIZE_GUIDELINE_AGENT_CAPS, size)
57
+ ? WORKFLOW_SIZE_GUIDELINE_AGENT_CAPS[size]
58
+ : undefined;
59
+ }
60
+ /** 规模判定。纯函数:每个环境量都由调用方注入。 */
61
+ export function evaluateWorkflowSize(input) {
62
+ const agentCap = positive(input.envAgentCap) ?? guidelineCap(input.guidelineSize) ?? WORKFLOW_SIZE_WARNING_AGENT_CAP;
63
+ const tokenCap = positive(input.envTokenCap) ?? WORKFLOW_SIZE_WARNING_TOKEN_CAP;
64
+ const scheduled = count(input.totalCount);
65
+ const tokens = count(input.tokens);
66
+ const base = {
67
+ agentCap,
68
+ tokenCap,
69
+ ...(scheduled !== undefined ? { scheduledAgents: scheduled } : {}),
70
+ ...(tokens !== undefined ? { totalTokens: tokens } : {}),
71
+ };
72
+ if (input.ultracodeActive === true)
73
+ return { kind: 'ok', suppressed: true, ...base };
74
+ // 分母:真值 / 两键下界 / 判不出。
75
+ const startedWire = count(input.startedCount);
76
+ const done = count(input.doneCount);
77
+ const failed = count(input.failedCount);
78
+ const bound = done !== undefined && failed !== undefined ? done + failed : undefined;
79
+ let started;
80
+ let lowerBound = false;
81
+ if (startedWire !== undefined && (bound === undefined || startedWire >= bound)) {
82
+ started = startedWire;
83
+ }
84
+ else if (bound !== undefined) {
85
+ // `startedCount` 缺席,或它比已完成数还小(帧间竞态:已完成的 agent 必然起过 ⇒ 这份 started 是陈旧的)。
86
+ // 顶上来的 `done + failed` 只是**下界**,不是真值 —— 与缺席同一处置。
87
+ started = bound;
88
+ lowerBound = true;
89
+ }
90
+ // 投影:分母是真值 ⇒ 投影;分母是下界(> 0)⇒ 只有上界;其余判不出。
91
+ let projected;
92
+ let upperBound;
93
+ if (scheduled !== undefined && tokens !== undefined && started !== undefined) {
94
+ if (!lowerBound) {
95
+ projected = Math.max(tokens, Math.round((started > 0 ? tokens / started : WORKFLOW_SIZE_WARNING_TOKENS_PER_AGENT) * scheduled));
96
+ }
97
+ else if (started > 0) {
98
+ upperBound = Math.max(tokens, Math.round((tokens / started) * scheduled));
99
+ }
100
+ }
101
+ const facts = {
102
+ ...base,
103
+ ...(projected !== undefined ? { projectedTokens: projected } : {}),
104
+ ...(upperBound !== undefined ? { projectedTokensUpperBound: upperBound } : {}),
105
+ ...(lowerBound && scheduled !== undefined && tokens !== undefined ? { startedIsLowerBound: true } : {}),
106
+ };
107
+ /** 投影轴在帽内的证据:真投影在帽内,或上界在帽内。 */
108
+ const projectionWithinCap = (projected !== undefined && projected <= tokenCap) || (upperBound !== undefined && upperBound <= tokenCap);
109
+ const overAgents = scheduled !== undefined && scheduled > agentCap;
110
+ const overTokens = (tokens !== undefined && tokens > tokenCap) || (projected !== undefined && projected > tokenCap);
111
+ if (overAgents || overTokens) {
112
+ return { kind: 'warn', axis: overAgents && overTokens ? 'both' : overAgents ? 'agents' : 'tokens', ...facts };
113
+ }
114
+ const missing = [];
115
+ if (scheduled === undefined)
116
+ missing.push('totalCount');
117
+ if (tokens === undefined)
118
+ missing.push('tokens');
119
+ if (scheduled !== undefined && tokens !== undefined && !projectionWithinCap)
120
+ missing.push('startedCount');
121
+ if (missing.length > 0)
122
+ return { kind: 'unknown', missing, ...facts };
123
+ return { kind: 'ok', ...facts };
124
+ }
125
+ /**
126
+ * 两个 env 覆盖的读口。`env` 由调用方交(本包不碰 `process.env`)。每组按 {@link WORKFLOW_SIZE_WARNING_ENV_NAMES}
127
+ * 的顺序取**第一个在场的名字**;那个名字的值不是正有限数 ⇒ 该键不在场,**不回落**到第二个名字
128
+ * (显式写错不该被悄悄换源)。
129
+ */
130
+ export function readWorkflowSizeWarningEnvCaps(env) {
131
+ const pick = (names) => {
132
+ if (env === undefined || env === null)
133
+ return undefined;
134
+ for (const n of names) {
135
+ const raw = env[n];
136
+ if (typeof raw !== 'string')
137
+ continue; // 空串也是「在场」:在场的坏值就地止步,不回落
138
+ return positive(Number(raw));
139
+ }
140
+ return undefined;
141
+ };
142
+ const a = pick(WORKFLOW_SIZE_WARNING_ENV_NAMES.agents);
143
+ const t = pick(WORKFLOW_SIZE_WARNING_ENV_NAMES.tokens);
144
+ return { ...(a !== undefined ? { envAgentCap: a } : {}), ...(t !== undefined ? { envTokenCap: t } : {}) };
145
+ }
146
+ /** 宽度阶梯:挑放得下的最长一档;连字形都放不下 ⇒ `undefined`(整段不渲)。`width` 由调用方注入(与渲染器同一把尺)。 */
147
+ export function workflowSizeWarningTextForWidth(available, width) {
148
+ if (available >= width(WORKFLOW_SIZE_WARNING_TEXT_LONG))
149
+ return WORKFLOW_SIZE_WARNING_TEXT_LONG;
150
+ if (available >= width(WORKFLOW_SIZE_WARNING_TEXT_SHORT))
151
+ return WORKFLOW_SIZE_WARNING_TEXT_SHORT;
152
+ if (available >= width(WORKFLOW_SIZE_WARNING_TEXT_MIN))
153
+ return WORKFLOW_SIZE_WARNING_TEXT_MIN;
154
+ return undefined;
155
+ }
@@ -91,6 +91,8 @@ export interface ApprovalsFeedStats {
91
91
  snapshots: number;
92
92
  /** 0.72.14:对账节拍真取件的次数(与 `polls` 分开记 —— 它不属于回落轮询腿)。 */
93
93
  reconciles: number;
94
+ /** 0.73.4:「欠一次看」的重试取件次数(见 `ApprovalsFeedOptions.pushTakeRetryMs`)。 */
95
+ pushTakeRetries: number;
94
96
  }
95
97
  /**
96
98
  * 0.72.14 **对账节拍**:悬挂的流内 ask 出现 / 结算时,较老的引擎不在 `approvals.stream` 上发任何事件 ⇒ push 腿连着也
@@ -132,6 +134,16 @@ export interface ApprovalsFeedOptions {
132
134
  * · **`refresh()`** 不受管 —— 那是端自己按下的显式动作。
133
135
  */
134
136
  shouldPoll?: () => boolean;
137
+ /**
138
+ * 0.73.4:**欠一次看**的重试基础间隔(ms;缺省 1000;`<= 0` = 关,与 0.73.3 之前同)。
139
+ *
140
+ * push 腿收到事件 = 引擎说「变了」;那一次 `list()` 若失败(抛出,或回体读不懂),这个「变了」就没人去看:
141
+ * 其后的 heartbeat 不触发取件,没有别的事件的话,那张待批的卡一直不出现。⇒ 任何一次 push 侧取件
142
+ * (起手那次、事件触发的、`refresh()`)没看成,就记一笔欠账并按 base、2×base、4×base … 退避重试,
143
+ * 间隔封顶 30 s 与 base 的较大者,**不按次数放弃**(有人可能正在等这张卡);**比欠账更晚发起**的任何一次
144
+ * 取件看成(哪条腿都算)即销账、撤定时器、间隔回到 base;更早发起、迟到回来的成功不销账。只在 push 模式记账 —— 轮询模式下轮询腿自己会再来。
145
+ */
146
+ pushTakeRetryMs?: number;
135
147
  /** 0.72.14:对账节拍(见 {@link ApprovalsFeedReconcileOptions})。缺省不起。 */
136
148
  reconcile?: ApprovalsFeedReconcileOptions;
137
149
  signal?: AbortSignal;
@@ -69,7 +69,7 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
69
69
  let takeSeq = 0;
70
70
  let lastObservedSeq = 0;
71
71
  const stats = {
72
- pushEvents: 0, heartbeats: 0, polls: 0, listErrors: 0, streamFailures: 0, snapshots: 0, reconciles: 0,
72
+ pushEvents: 0, heartbeats: 0, polls: 0, listErrors: 0, streamFailures: 0, snapshots: 0, reconciles: 0, pushTakeRetries: 0,
73
73
  };
74
74
  /** 域词表-14 收编:「怎么调 unref」的单一实现见 unrefTimer.ts,本函数只是就地起个短名。 */
75
75
  function arm(t) {
@@ -101,6 +101,56 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
101
101
  }
102
102
  return verdict;
103
103
  }
104
+ // ── 0.73.4 欠一次看(见 `pushTakeRetryMs` 头注)──
105
+ const pushTakeRetryBaseMs = opts?.pushTakeRetryMs ?? 1000;
106
+ const pushTakeRetryMaxMs = Math.max(pushTakeRetryBaseMs, 30_000);
107
+ let pushTakeRetryTimer;
108
+ let pushTakeRetryDelayMs = pushTakeRetryBaseMs;
109
+ /** 欠账锚在**发起序号**上:最近一次没看成的 push 侧取件是第几号(0 = 不欠)。 */
110
+ let owedSinceSeq = 0;
111
+ /** 已经看成的最晚发起序号(与欠账独立记):比它更早发起的失败是迟到的旧账,不记(对偶的乱序形)。 */
112
+ let lastLookedSeq = 0;
113
+ /**
114
+ * 销账只认**比欠账更晚发起**的取件:更早发起的那一次看的是失败之前的世界,它迟到的成功说明不了
115
+ * 「引擎说变了的那一下」已经有人看过(乱序是真的会发生的:慢 list / `refresh()` 与 push 重取撞车)。
116
+ */
117
+ function noteTakeLooked(seq) {
118
+ if (seq > lastLookedSeq)
119
+ lastLookedSeq = seq;
120
+ if (seq <= owedSinceSeq)
121
+ return;
122
+ owedSinceSeq = 0;
123
+ if (pushTakeRetryTimer !== undefined) {
124
+ clearTimeout(pushTakeRetryTimer);
125
+ pushTakeRetryTimer = undefined;
126
+ }
127
+ pushTakeRetryDelayMs = pushTakeRetryBaseMs;
128
+ }
129
+ function noteTakeMissed(via, seq) {
130
+ if (via !== 'push' || pushTakeRetryBaseMs <= 0)
131
+ return;
132
+ if (seq < lastLookedSeq)
133
+ return; // 更晚发起的一眼已经看成 ⇒ 这次迟到的失败不欠
134
+ if (seq > owedSinceSeq)
135
+ owedSinceSeq = seq;
136
+ if (stopped || ac.signal.aborted)
137
+ return;
138
+ if (pushTakeRetryTimer !== undefined)
139
+ return; // 幂等:已经有一只在排
140
+ const delay = pushTakeRetryDelayMs;
141
+ pushTakeRetryDelayMs = Math.min(pushTakeRetryDelayMs * 2, pushTakeRetryMaxMs);
142
+ pushTakeRetryTimer = arm(setTimeout(() => {
143
+ pushTakeRetryTimer = undefined;
144
+ if (stopped || ac.signal.aborted)
145
+ return;
146
+ stats.pushTakeRetries++;
147
+ // 欠账不随模式走:期间若降级到轮询,这一次照取(轮询腿可能被 shouldPoll 关着)。
148
+ void take('push').catch(e => {
149
+ // 重试自己逃逸(回体坏形):欠账已在 take() 内续记并再排,这里只接住 rejection。
150
+ hostLog('debug', `approvalsFeed: push-take retry escaped (unreadable body?) — still owed: ${String(e)}`);
151
+ });
152
+ }, delay));
153
+ }
104
154
  async function take(via,
105
155
  /** [F4957-1 二审] 发起这次取件时的轮询代际(只有轮询腿传;push/refresh 不受代际管)。 */
106
156
  leg) {
@@ -118,6 +168,7 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
118
168
  if (reading.kind === 'malformed') {
119
169
  stats.listErrors++;
120
170
  hostLog('debug', `approvalsFeed: list() body has an unreadable livePending section (${via} leg) — snapshot not committed`);
171
+ noteTakeMissed(via, seq);
121
172
  return;
122
173
  }
123
174
  if (reading.kind === 'present') {
@@ -129,11 +180,25 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
129
180
  catch (e) {
130
181
  stats.listErrors++;
131
182
  hostLog('debug', `approvalsFeed: list() failed (${via} leg): ${String(e)}`);
183
+ noteTakeMissed(via, seq);
132
184
  return;
133
185
  }
134
186
  if (stopped)
135
187
  return;
136
- const d = digestOf(rows, live);
188
+ // 🔴 摘要排在销账**之前**:回体坏形(例:`pending` 不是数组)会让它抛出、整次取件逃逸(各调用点自己接盘,
189
+ // 轮询腿的连续逃逸计数靠的就是这条路)。先销账再抛 = 一份读不懂的回体把欠账销了,之后再没人来看。
190
+ // 逃逸本身也是「没看成」:在**这里**统一记欠账再原样抛出,四个调用点(起手 / 事件 / refresh / 重试)不必各记各的。
191
+ let d;
192
+ try {
193
+ d = digestOf(rows, live);
194
+ }
195
+ catch (e) {
196
+ noteTakeMissed(via, seq);
197
+ throw e;
198
+ }
199
+ // 看成了(回体读得懂)⇒ 销掉**更早发起**的欠账(判据在发起序号上,见 noteTakeLooked;与下面两道提交闸无关:
200
+ // 销账问的是「失败之后有没有人再看过」,提交闸问的是「这份结论还新不新」)。
201
+ noteTakeLooked(seq);
137
202
  // 🔴 [F4957-1 二审 finding②] 代际闸必须排在**提交之前**:下面四行(lastDigest / revision /
138
203
  // last / onSnapshot)一落,视图就已经被改了 —— 守卫排在 take() 之外只挡得住计数与续排,挡不住
139
204
  // 回滚。乱序是真的会发生的:旧 poll 在途 → push 重连并发布新快照 → 旧 poll 才带着**更老**的
@@ -407,6 +472,8 @@ export function startApprovalsFeed(client, onSnapshot, opts) {
407
472
  clearTimeout(retryTimer);
408
473
  if (reconcileTimer !== undefined)
409
474
  clearTimeout(reconcileTimer);
475
+ if (pushTakeRetryTimer !== undefined)
476
+ clearTimeout(pushTakeRetryTimer);
410
477
  opts?.signal?.removeEventListener('abort', onOuterAbort);
411
478
  ac.abort();
412
479
  },
@@ -558,6 +558,16 @@ releaseLatch) {
558
558
  'The plan approval was NOT applied: the engine refused "auto-accept edits" for this task (it was not started read-only in plan mode, so that option does not apply). ' +
559
559
  'Nothing changed engine-side — the plan is still waiting for review. Tell the user plainly, and that approving with "manually approve edits" will go through.';
560
560
  }
561
+ else if (status === 404 && refusedCode === 'not_found.plan_review') {
562
+ // 0.73.5:这道门已经不在了(早先的决断已生效,或已过期)。现场形:用户在一张**陈旧的重开卡**上作答 ⇒ 404;
563
+ // 通用句「decision failed: HTTP 404 …」被模型读成「计划被拒 / 计划失败」,于是对用户说计划被拒并重新规划 ——
564
+ // 而那份计划其实已批准并跑完。⇒ 明说这不是拒绝也不是失败,只是这次作答没有对象;效果态仍是 not_applied。
565
+ hostLog('debug', `planReviewWire: ${decision} → 404 not_found.plan_review (no pending plan review any more — an earlier decision likely settled it)`);
566
+ effect = 'not_applied';
567
+ outcome =
568
+ `This ${decision} was not applied: the engine no longer has a pending plan review for this task — an earlier decision most likely already went through (or the review expired). ` +
569
+ 'This is NOT a rejection of the plan and NOT a failure of the task; the answer just given simply had nothing left to act on.';
570
+ }
561
571
  else if (typeof status === 'number') {
562
572
  const msg = e instanceof Error ? e.message : String(e);
563
573
  hostLog('debug', `planReviewWire: ${decision} → ${status} ${msg.slice(0, 200)}`);
package/dist/index.d.ts CHANGED
@@ -227,6 +227,7 @@ export * from './headlessPermissionModeWire.js';
227
227
  export * from './headlessReconnectWire.js';
228
228
  export * from './fleet/fleetProjection.js';
229
229
  export * from './fleet/fleetLedger.js';
230
+ export * from './fleet/workflowSizeWarning.js';
230
231
  export * from './subagent/engineDelegatedPrompt.js';
231
232
  export * from './subagent/engineRowStopGate.js';
232
233
  export * from './subagent/engineSubagentOutput.js';
package/dist/index.js CHANGED
@@ -364,6 +364,8 @@ export * from './headlessReconnectWire.js';
364
364
  // · `engineWireTarget.ts` 的 `installed` —— 非 Node 宿主的显式 wire 目标。
365
365
  export * from './fleet/fleetProjection.js';
366
366
  export * from './fleet/fleetLedger.js';
367
+ // 0.73.5(CC-58):workflow 规模告警判定(三态;缺席 = 不知道,未知的规模不当作规模正常)。
368
+ export * from './fleet/workflowSizeWarning.js';
367
369
  export * from './subagent/engineDelegatedPrompt.js';
368
370
  export * from './subagent/engineRowStopGate.js';
369
371
  export * from './subagent/engineSubagentOutput.js';
@@ -896,6 +896,10 @@ modelText) {
896
896
  };
897
897
  }
898
898
  const wfName = typeof inner?.name === 'string' && inner.name.length > 0 ? inner.name : undefined;
899
+ // 0.73.4(CC-63):**run 级**错误码。引擎在这张结构卡的**顶层**铸 `errorCode`(与 `completionId` 同层;有值才带,
900
+ // 缺席 = 这条 run 没有码),今天只有 workflow 形带。它不是 `agent_runs[].errorCode`(per-agent,住在模型面 JSON 里),
901
+ // 也不在内层 `details` 上。开集原词透传;缺席 / 空串 / 非字符串 ⇒ 键不铸,绝不编码。CC 的 task 形没有这一格 ⇒ 超集键。
902
+ const runErrorCode = typeof s.errorCode === 'string' && s.errorCode.length > 0 ? s.errorCode : undefined;
899
903
  let output = bodyText ?? '';
900
904
  if (taskType === 'workflow') {
901
905
  // 终态轮询卡经过投影 = 模型 in-band 已收到结局 ⇒ 预 seed runId 去重(补发通道对同 runId 丢弃)。
@@ -916,6 +920,7 @@ modelText) {
916
920
  description: wfName ?? taskId,
917
921
  output,
918
922
  ...(error !== undefined ? { error } : {}),
923
+ ...(runErrorCode !== undefined ? { _sema_error_code: runErrorCode } : {}),
919
924
  },
920
925
  },
921
926
  };