@kanyun-ai-infra/agenthub 1.14.0 → 1.15.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/CHANGELOG.md CHANGED
@@ -2,6 +2,37 @@
2
2
 
3
3
  本文件从 1.12.0 开始记录;更早的版本见 git 历史中的 `chore(agenthub-sdk): release X.Y.Z` 提交。
4
4
 
5
+ ## 1.15.0
6
+
7
+ 浏览器实时语音入口新增播放对齐、页面工具执行与播放输出节点;其余为随共享契约内联的变化。
8
+
9
+ ### 新增(`@kanyun-ai-infra/agenthub/realtime`)
10
+
11
+ - `onTranscriptPlayed(transcript)`:assistant 转写片段在音频实际播放到该片段时逐段触发,载荷带墙钟 `playedAt`。依赖网关在转写帧上带 `audioOffsetMs`;没有偏移的旧网关不触发此回调,被用户打断清空的片段也不触发。
12
+ - `onPlaybackState(state)`:`speaking` / `listening`,按实际可闻的播放状态触发,与按模型生成进度的 `onState` 区分。队列播干、播放被打断或通话停止时回到 `listening`。
13
+ - `onTranscript` 的 assistant 帧新增可选 `audioOffsetMs`:该片段在本轮音频中的起始毫秒数,用于把字幕或画面切到正在播放的位置。
14
+ - `onToolCall(call)`:执行 `voice.realtime.tools` 声明的模型工具调用;`call` 含 `callId`、`name`、`args`,可选 `turnSeq`。返回值作为工具结果回传(JSON,至多 32 KiB)。未提供处理函数回 `tool_unavailable`,处理函数抛错回 `tool_failed`,结果超限回 `tool_result_too_large`;页面不答时网关 10 秒后自行以 `tool_timeout` 答复,不会把模型卡死。
15
+ - `outputNode(context)`:把播放输出接到页面自建的音频节点(如变调等后处理),由页面继续接到 `context.destination`。缺省直连输出。
16
+ - 新导出类型:`RealtimeTranscriptPlayed`、`RealtimePlaybackState`、`RealtimeToolCall`。
17
+
18
+ ### 随共享契约内联的变化
19
+
20
+ - agent 定义的 `voice.realtime` 新增可选 `tools`(1–16 个):每项含 `name`(匹配 `^[A-Za-z_][A-Za-z0-9_]{0,63}$`,重名拒绝)、`description`(1–1024 字符)与可选 `parameters`(JSON Schema 子集,嵌套至多 6 层、节点至多 128、序列化至多 8 KiB)。`publishVersion` / `applyManifest` 的本地校验接受该字段;不声明时行为不变。
21
+ - `voice.realtime` 新增可选 `greetOnConnect`:开启时网关在首通建立上游会话后替 agent 发一次初始文本回合,模型按 system prompt 主动开口;缺省或 `false` 关闭,只对首通生效,重连与接手不重发。
22
+
23
+ ### 会话
24
+
25
+ - `sessions.create` 带 `memorySubjects` 的请求现在可以领取预热池:声明的业务主体替换池模板铸出的主体;`idempotencyScope` / `idempotencyKey` 与 `memorySubjects` 不再使请求失去领取资格。本次只有注释与文档变化,领取行为由服务端决定;服务端未升级时请求仍照旧冷启。
26
+
27
+ ### 其他
28
+
29
+ - 语音开关的文档措辞改为 kill switch `voice_turns_disabled`(及同类 `*_DISABLED`),公开方法签名不变。
30
+ - 随包 `SKILL.md` 补充实时语音接入走查。
31
+
32
+ ### 迁移说明
33
+
34
+ - 使用 `onTranscriptPlayed`、`onPlaybackState`、`onToolCall` 或 `outputNode` 的接入方须升级到 `@kanyun-ai-infra/agenthub@1.15.0`。1.14.0 没有这些回调;旧 SDK 在声明工具的 agent 上会忽略 `tool_call`,模型要等网关 10 秒超时答复后才继续。
35
+
5
36
  ## 1.14.0
6
37
 
7
38
  ### 随共享契约内联的语音变化
package/dist/index.cjs CHANGED
@@ -3576,6 +3576,9 @@ var CreateSessionRequestSchema = import_zod10.z.object({
3576
3576
  *
3577
3577
  * 解析只发生一次、在会话创建事务内,结果冻结进 `sessions.memory_subjects`;写侧
3578
3578
  * (抽取写回)与读侧(召回)之后都只读那一列(I7 写读同源)。
3579
+ *
3580
+ * 带它的请求**照样能领预热池**:领取收尾在同一条 UPDATE 里按这份声明重算冻结列(替换池模板
3581
+ * 铸的业务主体,`[]` = 只剩 agent 主体);绑不进去就退冷启,不会交出没有声明主体的会话。
3579
3582
  */
3580
3583
  memorySubjects: import_zod10.z.array(SessionMemorySubjectDeclarationSchema).max(MAX_SESSION_MEMORY_SUBJECTS).optional(),
3581
3584
  /**
@@ -3596,8 +3599,9 @@ var CreateSessionRequestSchema = import_zod10.z.object({
3596
3599
  * `PILOT_END_USER_ID`、记忆主体(见下)、create 响应 `data.session.user`。
3597
3600
  *
3598
3601
  * **带它不吃池**:请求携带本字段即退出预热领用资格
3599
- * (`canServeFromPrewarmPool`),与 `memorySubjects` 同一条规则——池里暖好的会话
3600
- * 在调用方出现之前就冻结了身份,领用时换不掉。已知访客换 agent 应冷启带既有 id。
3602
+ * (`canServeFromPrewarmPool`)——池里暖好的会话在调用方出现之前就冻结了身份,领用时
3603
+ * 换不掉。已知访客换 agent 应冷启带既有 id。(`memorySubjects` 不受这条限制:它在领取
3604
+ * 收尾时绑定,见该字段。)
3601
3605
  *
3602
3606
  * **不隐含记忆声明**:本字段本身不使这个 id 成为任何记忆主体;要让记忆落到它
3603
3607
  * 名下,冷启路径须同时在 `memorySubjects` 里声明它(池路径由
@@ -4160,7 +4164,8 @@ var WorkloadManifestErrorCodeSchema = import_zod4.z.enum([
4160
4164
  // `statusForManifestError`. Additive widening = patch (see
4161
4165
  // `WORKLOAD_SLUG_TAKEN` above).
4162
4166
  // - `LIVE_AGENTS_DISABLED` — the Revision's snapshot is `format: live` while
4163
- // `live_agents_enabled` is off. Same criterion as before; it used to throw
4167
+ // the kill switch `live_agents_disabled` is on (before #5069: `live_agents_enabled`
4168
+ // off). Same criterion as before; it used to throw
4164
4169
  // the generic `MANIFEST_VALIDATION_ERROR`. Pipeline join reports the same
4165
4170
  // condition earlier as `FILE_AGENT_LIVE_DISABLED`.
4166
4171
  // - `LIVE_REVISION_UNSUPPORTED` — the merged, frozen live snapshot carries a
@@ -4515,7 +4520,79 @@ var RealtimeVoiceNameSchema = import_zod4.z.enum([
4515
4520
  "Sadaltager",
4516
4521
  "Sulafat"
4517
4522
  ]);
4518
- var WorkloadManifestRealtimeVoiceSchema = import_zod4.z.object({
4523
+ var RealtimeToolParameterSchema = import_zod4.z.lazy(
4524
+ () => import_zod4.z.object({
4525
+ type: import_zod4.z.enum(["string", "number", "integer", "boolean", "array", "object"]).optional(),
4526
+ description: import_zod4.z.string().max(1024).optional(),
4527
+ enum: import_zod4.z.array(import_zod4.z.string().max(256)).max(64).optional(),
4528
+ nullable: import_zod4.z.boolean().optional(),
4529
+ items: RealtimeToolParameterSchema.optional(),
4530
+ properties: import_zod4.z.record(import_zod4.z.string().max(64), RealtimeToolParameterSchema).optional(),
4531
+ required: import_zod4.z.array(import_zod4.z.string().max(64)).max(32).optional()
4532
+ }).strict()
4533
+ );
4534
+ function toolParameterProblem(parameters) {
4535
+ if (parameters === void 0) return void 0;
4536
+ let nodes = 0;
4537
+ let problem;
4538
+ const visit = (node, depth) => {
4539
+ var _a, _b;
4540
+ if (problem !== void 0) return;
4541
+ nodes += 1;
4542
+ if (depth > 6) {
4543
+ problem = "tool parameters nest deeper than 6 levels";
4544
+ return;
4545
+ }
4546
+ if (nodes > 128) {
4547
+ problem = "tool parameters contain more than 128 nodes";
4548
+ return;
4549
+ }
4550
+ if (node.type === "array" && node.items === void 0) {
4551
+ problem = "an array parameter must declare items";
4552
+ return;
4553
+ }
4554
+ if (node.required !== void 0) {
4555
+ const declared = new Set(Object.keys((_a = node.properties) != null ? _a : {}));
4556
+ const missing = node.required.find((name) => !declared.has(name));
4557
+ if (missing !== void 0) {
4558
+ problem = `required parameter ${missing} is not declared in properties`;
4559
+ return;
4560
+ }
4561
+ }
4562
+ for (const child of Object.values((_b = node.properties) != null ? _b : {})) visit(child, depth + 1);
4563
+ if (node.items !== void 0) visit(node.items, depth + 1);
4564
+ };
4565
+ visit(parameters, 1);
4566
+ if (problem !== void 0) return problem;
4567
+ if (new TextEncoder().encode(JSON.stringify(parameters)).length > 8192)
4568
+ return "tool parameters exceed 8192 bytes";
4569
+ return void 0;
4570
+ }
4571
+ var WORKLOAD_MANIFEST_REALTIME_TOOLS_MAX_LENGTH = 16;
4572
+ var WorkloadManifestRealtimeToolSchema = import_zod4.z.object({
4573
+ name: import_zod4.z.string().regex(
4574
+ /^[A-Za-z_][A-Za-z0-9_]{0,63}$/,
4575
+ "tool name must be an identifier of at most 64 chars"
4576
+ ),
4577
+ description: import_zod4.z.string().min(1).max(1024),
4578
+ parameters: RealtimeToolParameterSchema.optional()
4579
+ }).strict();
4580
+ var WorkloadManifestRealtimeToolListSchema = import_zod4.z.array(WorkloadManifestRealtimeToolSchema).min(1).max(WORKLOAD_MANIFEST_REALTIME_TOOLS_MAX_LENGTH).superRefine((tools, ctx) => {
4581
+ const names = /* @__PURE__ */ new Set();
4582
+ for (const [index, tool] of tools.entries()) {
4583
+ if (names.has(tool.name))
4584
+ ctx.addIssue({
4585
+ code: "custom",
4586
+ path: [index, "name"],
4587
+ message: "tool names must be unique"
4588
+ });
4589
+ names.add(tool.name);
4590
+ const problem = toolParameterProblem(tool.parameters);
4591
+ if (problem !== void 0)
4592
+ ctx.addIssue({ code: "custom", path: [index, "parameters"], message: problem });
4593
+ }
4594
+ });
4595
+ var RealtimeVoiceWireSchema = import_zod4.z.object({
4519
4596
  provider: import_zod4.z.literal("vertex-gemini-live"),
4520
4597
  model: import_zod4.z.enum(["gemini-3.8-live", "gemini-live-2.5-flash-native-audio"]),
4521
4598
  voice: RealtimeVoiceNameSchema,
@@ -4526,7 +4603,20 @@ var WorkloadManifestRealtimeVoiceSchema = import_zod4.z.object({
4526
4603
  return false;
4527
4604
  }
4528
4605
  }, "language must be a BCP-47 language tag").optional(),
4529
- turnDetection: import_zod4.z.object({ silenceMs: import_zod4.z.number().int().positive() }).strict().optional()
4606
+ turnDetection: import_zod4.z.object({ silenceMs: import_zod4.z.number().int().positive() }).strict().optional(),
4607
+ /**
4608
+ * 接通即开讲(#5221):开启时网关在通话首次建立上游会话后替 agent 发一次初始文本回合,
4609
+ * 模型按 system prompt 主动开口。缺省 / `false` 关闭,行为与声明前一致。只对首通生效:
4610
+ * 上游重连与带原 callId 的接手不重发。
4611
+ */
4612
+ greetOnConnect: import_zod4.z.boolean().optional()
4613
+ }).strict();
4614
+ var WorkloadManifestRealtimeVoiceSchema = RealtimeVoiceWireSchema.extend({
4615
+ /**
4616
+ * Declared client tools. Optional and absent by default: an agent without it behaves exactly
4617
+ * as before, and the claim to an older gateway carries no tools key at all.
4618
+ */
4619
+ tools: WorkloadManifestRealtimeToolListSchema.optional()
4530
4620
  }).strict();
4531
4621
  var WorkloadManifestVoiceSchema = import_zod4.z.object({
4532
4622
  realtime: WorkloadManifestRealtimeVoiceSchema.optional(),
@@ -8884,6 +8974,20 @@ var EnvironmentRepointRecordSchema = import_zod59.z.object({
8884
8974
  actor: import_zod59.z.string().nullable(),
8885
8975
  createdAt: import_zod59.z.coerce.date()
8886
8976
  }).strict();
8977
+ var EnvironmentExecutorWarningRevisionSchema = import_zod59.z.object({
8978
+ revisionId: import_zod59.z.string().uuid(),
8979
+ /** Which pointer of the slot carries the Revision. */
8980
+ arm: import_zod59.z.enum(["stable", "canary"]),
8981
+ format: import_zod59.z.string(),
8982
+ protocolVersion: import_zod59.z.string(),
8983
+ /** `absent` = judged as ENVCAP-3 (no readable declaration for the image). */
8984
+ declaration: import_zod59.z.enum(["absent", "present"]),
8985
+ /** Where the judged image came from; `kind` discriminates the remaining keys. */
8986
+ capabilitySource: import_zod59.z.object({ kind: import_zod59.z.string() }).passthrough()
8987
+ }).strict();
8988
+ var EnvironmentExecutorWarningSchema = import_zod59.z.object({
8989
+ revisions: import_zod59.z.array(EnvironmentExecutorWarningRevisionSchema).min(1)
8990
+ }).strict();
8887
8991
  var EnvironmentReferenceSchema = import_zod59.z.object({
8888
8992
  workloadId: import_zod59.z.string().uuid(),
8889
8993
  workloadSlug: import_zod59.z.string(),
@@ -9691,7 +9795,7 @@ var VOICE_ERROR_CODES = {
9691
9795
  REALTIME_CAPACITY_EXCEEDED: 409,
9692
9796
  /** The platform concurrency store (realtime Redis) is unavailable; retry. */
9693
9797
  REALTIME_ADMISSION_UNAVAILABLE: 503,
9694
- /** `voice_turns_enabled` 关态时任何语音回合派发被拒(紧急停止;VT-1)。 */
9798
+ /** 紧急关闭 `voice_turns_disabled` 打开时任何语音回合派发被拒(VT-1)。 */
9695
9799
  VOICE_TURNS_DISABLED: 400,
9696
9800
  /** 语音附件时长 >60 秒或大小 >2MB(VA-3,上限只在语音路径)。 */
9697
9801
  VOICE_INPUT_TOO_LONG: 400,
@@ -11617,8 +11721,10 @@ var EvalTriggerSchema = import_zod82.z.enum([
11617
11721
  ]);
11618
11722
  var EvalSeveritySchema = import_zod82.z.enum(["gate", "soft"]);
11619
11723
  var EvalGraderTypeSchema = import_zod82.z.enum(["transcript", "file", "llm", "script", "reply"]);
11724
+ var EvalLlmToolsSchema = import_zod82.z.enum(["full", "calls", "none"]);
11620
11725
  var EvalPassPolicySchema = import_zod82.z.enum(["all", "any"]);
11621
11726
  var EvalSuitePurposeSchema = import_zod82.z.enum(["regression", "capability"]);
11727
+ var EvalSuiteGateDowngradeSchema = import_zod82.z.enum(["allow", "never"]);
11622
11728
  var EVAL_SKIP_REASON_MAX = 2048;
11623
11729
  var EvalSuiteSkipSchema = import_zod82.z.object({ reason: import_zod82.z.string().min(1).max(EVAL_SKIP_REASON_MAX) }).strict();
11624
11730
  var EvalSkippedSuiteSchema = import_zod82.z.object({ path: import_zod82.z.string().min(1), reason: import_zod82.z.string().min(1) }).strict();
@@ -11687,6 +11793,10 @@ var EvalFileShorthandExpectSchema = import_zod82.z.object({
11687
11793
  matches: import_zod82.z.string().optional()
11688
11794
  // 正则字符串
11689
11795
  }).strict();
11796
+ var EvalFileToolSourceSchema = import_zod82.z.object({
11797
+ name: import_zod82.z.string().min(1),
11798
+ part: import_zod82.z.enum(["input", "output", "both"]).optional()
11799
+ }).strict();
11690
11800
  var EvalFileGraderSchema = import_zod82.z.object({
11691
11801
  type: import_zod82.z.literal("file"),
11692
11802
  label: EvalGraderLabelSchema,
@@ -11696,7 +11806,12 @@ var EvalFileGraderSchema = import_zod82.z.object({
11696
11806
  files: import_zod82.z.array(EvalFileEntrySchema).min(1).optional(),
11697
11807
  // 简写形式
11698
11808
  path: EvalRelativePathSchema.optional(),
11699
- expect: EvalFileShorthandExpectSchema.optional()
11809
+ expect: EvalFileShorthandExpectSchema.optional(),
11810
+ /**
11811
+ * #5215:内容来源 = 工具产物而非工作区文件。与 `path` / `files` 恰用其一,
11812
+ * 且必须搭配 `expect`(suite 级 superRefine 校验)。
11813
+ */
11814
+ tool: EvalFileToolSourceSchema.optional()
11700
11815
  }).strict();
11701
11816
  var EvalLlmGraderSchema = import_zod82.z.object({
11702
11817
  type: import_zod82.z.literal("llm"),
@@ -11708,6 +11823,14 @@ var EvalLlmGraderSchema = import_zod82.z.object({
11708
11823
  }),
11709
11824
  dimensions: import_zod82.z.array(import_zod82.z.string().min(1)).optional(),
11710
11825
  threshold: import_zod82.z.number().min(0).max(1),
11826
+ /**
11827
+ * #5215:判官材料是否带工具调用。缺省 `none`(只判回复文本,与既有 suite 的
11828
+ * 判分材料逐字节一致)。optional 字段叠加为增量变更、不升 apiVersion;老
11829
+ * worker 会以 `Unrecognized key` strict 拒(发布次序 = control-worker 先行)。
11830
+ * 工具段改变判分输入,但声明值在 case 对象里、进 `case_definition_digest`,
11831
+ * 因此不升 `engineVersion`(换值即换基线桶,不靠 engineVersion 区分)。
11832
+ */
11833
+ tools: EvalLlmToolsSchema.optional(),
11711
11834
  // 上界对齐 eval_grader_results.grader_version(PG int4,B1 硬列):graderVersion
11712
11835
  // 是**作者可写**的单调整数,时间戳当版本号(Date.now() ≈ 1.7e12)一次就越界——
11713
11836
  // 越界在解析期 400 告知作者,而不是等写明细行时 22003 连累父行回滚、整个 run
@@ -11754,12 +11877,17 @@ var EvalScriptGraderSchema = import_zod82.z.object({
11754
11877
  expect: EvalScriptExpectSchema.optional()
11755
11878
  }).strict();
11756
11879
  var EvalReplyExpectSchema = import_zod82.z.object({
11880
+ /** 字面子串,不是正则。 */
11757
11881
  includes: import_zod82.z.string().min(1).optional(),
11882
+ /** 字面子串,不是正则;「不许出现某段话」的确定写法见 `notMatches`。 */
11758
11883
  excludes: import_zod82.z.string().min(1).optional(),
11759
11884
  equals: import_zod82.z.string().optional(),
11760
11885
  // 正则字符串;无效正则不在 schema 层拒——按 file grader 的 safeRegex 姿势在
11761
11886
  // 判分侧判"不匹配",交给断言侧报 failed(不 errored)。
11762
- matches: import_zod82.z.string().min(1).optional()
11887
+ matches: import_zod82.z.string().min(1).optional(),
11888
+ // 正则字符串,不允许匹配(`excludes` 的正则版)。无效正则**判 failed**而不是
11889
+ // passed:编译不出来时「没有匹配」是假的,绝不能因此绿灯(#5209)。
11890
+ notMatches: import_zod82.z.string().min(1).optional()
11763
11891
  }).strict();
11764
11892
  var EvalReplyGraderSchema = import_zod82.z.object({
11765
11893
  type: import_zod82.z.literal("reply"),
@@ -12003,12 +12131,28 @@ var EvalSuiteSchema = import_zod82.z.object({
12003
12131
  kind: import_zod82.z.literal(EVAL_SUITE_KIND),
12004
12132
  purpose: EvalSuitePurposeSchema.optional(),
12005
12133
  // U2,默认 regression
12134
+ gateDowngrade: EvalSuiteGateDowngradeSchema.optional(),
12135
+ // #5216,默认 allow
12006
12136
  skip: EvalSuiteSkipSchema.optional(),
12007
12137
  // §4 声明跳过(带必填理由)
12138
+ /**
12139
+ * #5215:pipeline 面按 **advisory** 跑——照常建 eval run 并后台执行,但不进
12140
+ * eval step 的等待、不进 conclusion 折叠,**不挡门禁**。结果仍挂在结论的
12141
+ * `evalRunIds` 下,流水线详情可见。缺省 false。与 `skip` 互斥(拒绝组合:
12142
+ * 一个说「不跑」一个说「跑但不判」,语义冲突)。
12143
+ */
12144
+ advisory: import_zod82.z.boolean().optional(),
12008
12145
  defaults: EvalSuiteDefaultsSchema.optional(),
12009
12146
  cases: import_zod82.z.array(EvalCaseSchema).min(1)
12010
12147
  }).strict().superRefine((suite, ctx) => {
12011
12148
  var _a, _b;
12149
+ if (suite.advisory === true && suite.skip != null) {
12150
+ ctx.addIssue({
12151
+ code: import_zod82.z.ZodIssueCode.custom,
12152
+ path: ["advisory"],
12153
+ message: "`advisory: true` and `skip` are mutually exclusive \u2014 skip means the suite does not run in the pipeline at all; advisory means it runs but is excluded from the gate"
12154
+ });
12155
+ }
12012
12156
  const seen = /* @__PURE__ */ new Set();
12013
12157
  suite.cases.forEach((c, ci) => {
12014
12158
  var _a2, _b2, _c, _d, _e;
@@ -12131,11 +12275,15 @@ var EvalSuiteSchema = import_zod82.z.object({
12131
12275
  if (g.type === "file") {
12132
12276
  const hasFiles = g.files != null;
12133
12277
  const hasShorthand = g.path != null && g.expect != null;
12134
- if (hasFiles === hasShorthand) {
12278
+ const hasTool = g.tool != null && g.expect != null;
12279
+ const sourceCount = [hasFiles, hasShorthand, hasTool].filter(Boolean).length;
12280
+ const strayPath = g.path != null && g.expect == null;
12281
+ const strayTool = g.tool != null && g.expect == null;
12282
+ if (sourceCount !== 1 || strayTool || strayPath && !hasFiles) {
12135
12283
  ctx.addIssue({
12136
12284
  code: import_zod82.z.ZodIssueCode.custom,
12137
12285
  path: ["cases", ci, "graders", gi],
12138
- message: "file grader must use either `files[]` or `{path, expect}`, not both/neither"
12286
+ message: "file grader must use exactly one of `files[]`, `{path, expect}` or `{tool, expect}` (tool = assert on a tool call's input/output instead of a workspace file); a bare `path` without `expect` is ignored only when `files[]` is present (legacy tolerance)"
12139
12287
  });
12140
12288
  }
12141
12289
  }
@@ -12173,12 +12321,12 @@ var EvalSuiteSchema = import_zod82.z.object({
12173
12321
  }
12174
12322
  const e = g.expect;
12175
12323
  const hasEquals = e.equals != null;
12176
- const hasOthers = e.includes != null || e.excludes != null || e.matches != null;
12324
+ const hasOthers = e.includes != null || e.excludes != null || e.matches != null || e.notMatches != null;
12177
12325
  if (hasEquals && hasOthers) {
12178
12326
  ctx.addIssue({
12179
12327
  code: import_zod82.z.ZodIssueCode.custom,
12180
12328
  path: ["cases", ci, "graders", gi, "expect"],
12181
- message: "reply grader `expect.equals` is mutually exclusive with includes/excludes/matches"
12329
+ message: "reply grader `expect.equals` is mutually exclusive with includes/excludes/matches/notMatches"
12182
12330
  });
12183
12331
  }
12184
12332
  if (!hasEquals && !hasOthers) {
@@ -13021,7 +13169,7 @@ var OnlineReplyGraderSchema = EvalReplyGraderSchema.pick({ type: true, label: tr
13021
13169
  threshold: forbiddenThreshold,
13022
13170
  expect: EvalReplyExpectSchema.pick({ excludes: true, matches: true })
13023
13171
  }).strict();
13024
- var OnlineLlmToolsSchema = import_zod83.z.enum(["full", "calls", "none"]);
13172
+ var OnlineLlmToolsSchema = EvalLlmToolsSchema;
13025
13173
  var onlineLlmFields = EvalLlmGraderSchema.pick({
13026
13174
  type: true,
13027
13175
  label: true,
@@ -13727,7 +13875,12 @@ var RealtimeClaimResponseSchema = import_zod87.z.object({
13727
13875
  credentialSource: import_zod87.z.enum(REALTIME_CREDENTIAL_SOURCES).optional(),
13728
13876
  config: import_zod87.z.object({
13729
13877
  systemPrompt: import_zod87.z.string(),
13730
- voice: WorkloadManifestRealtimeVoiceSchema,
13878
+ voice: RealtimeVoiceWireSchema,
13879
+ /**
13880
+ * Client-executed tools frozen with the call. Stripped by readers that do not know it, which
13881
+ * is the intended rolling-deploy story: an older gateway answers the call without tools.
13882
+ */
13883
+ tools: WorkloadManifestRealtimeToolListSchema.optional(),
13731
13884
  recording: import_zod87.z.boolean().default(false),
13732
13885
  upstream: RealtimeUpstreamCredentialSchema.optional()
13733
13886
  })
@@ -15464,13 +15617,13 @@ var SessionsResource = class {
15464
15617
  * The codes and their statuses are the server's voice error-code table.
15465
15618
  *
15466
15619
  * **`VOICE_TURNS_DISABLED` is NOT one of them on this path**, despite being
15467
- * in that table: it answers a NON-empty-text turn while `voice_turns_enabled`
15468
- * is off, and this method always sends `text: ""`. The switch being off is
15620
+ * in that table: it answers a NON-empty-text turn while the kill switch
15621
+ * `voice_turns_disabled` is on, and this method always sends `text: ""`. The switch being on is
15469
15622
  * refused one step earlier as the ordinary empty-text `400`
15470
15623
  * `VALIDATION_ERROR` (issue path `["text"]`).
15471
15624
  *
15472
15625
  * **Four different causes produce that one 400, and the body does not
15473
- * distinguish them**: the switch off; the agent not declaring `voice.input`;
15626
+ * distinguish them**: the switch on; the agent not declaring `voice.input`;
15474
15627
  * an extension outside the voice set; and an attachment that never looks like
15475
15628
  * audio at all — raw bytes passed without `contentType` upload as
15476
15629
  * `application/octet-stream` and fail the empty-text rule in