@duvoai/cli 1.91.0 → 1.92.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.
Files changed (2) hide show
  1. package/dist/bin/duvo.js +165 -43
  2. package/package.json +1 -1
package/dist/bin/duvo.js CHANGED
@@ -127,6 +127,9 @@ __name2(readCopilotStepType, "readCopilotStepType");
127
127
  var errorSchema = z22.object({
128
128
  error: z22.string()
129
129
  });
130
+ var codedErrorSchema = errorSchema.extend({
131
+ code: z22.string().optional()
132
+ });
130
133
  function extensibleEnum(values, meta = {}) {
131
134
  return z3.enum(values).meta({
132
135
  ...meta,
@@ -7575,11 +7578,10 @@ var ORG_ROLE = {
7575
7578
  * A guest's reach is deliberately identical to a team member holding no
7576
7579
  * membership row at all, with two exceptions. The organization appears in
7577
7580
  * their switcher (`/organizations/mine` is self-scoped and ungated), where
7578
- * the row is rendered inert. Clarity landscape writes accept a guest whose
7579
- * team role allows them: placing their own team's work into the shared area
7580
- * structure, and adding areas as a team Manager
7581
- * (`ORG_ROLES_WITH_TEAM_MEMBERSHIP` in
7582
- * backend-node/src/modules/clarity-hierarchy/access.ts).
7581
+ * the row is rendered inert. Clarity landscape targets owned by one of their
7582
+ * teams are decided on the team role alone (`resolveLandscapeEditScope` in
7583
+ * backend-node/src/modules/clarity-hierarchy/access.ts), and the org reads in
7584
+ * {@link ORG_ROLES.TEAM_MEMBER_READS} include them.
7583
7585
  * See specs/org-owned-resources/PHASE_0_PLAN.md.
7584
7586
  */
7585
7587
  GUEST: "organization:guest"
@@ -7625,6 +7627,18 @@ var ORG_ROLES = {
7625
7627
  ORG_ROLE.ADMIN,
7626
7628
  ORG_ROLE.MEMBER
7627
7629
  ],
7630
+ /**
7631
+ * Org reads a team-only member (`GUEST`) needs to work in their teams: the
7632
+ * org logo in the switcher and the connection-sharing settings that decide
7633
+ * what their team's Connections pages offer. Org-wide data stays on `ALL`.
7634
+ */
7635
+ TEAM_MEMBER_READS: [
7636
+ ORG_ROLE.EXECUTIVE,
7637
+ ORG_ROLE.OWNER,
7638
+ ORG_ROLE.ADMIN,
7639
+ ORG_ROLE.MEMBER,
7640
+ ORG_ROLE.GUEST
7641
+ ],
7628
7642
  // Org roles an SSO connection may auto-grant to every user it admits.
7629
7643
  // Excludes executive/owner: default_org_role is applied unconditionally to
7630
7644
  // every SSO login, so those tiers must never be broadly granted by config.
@@ -7890,7 +7904,6 @@ var INTEGRATION_SLUGS = [
7890
7904
  "exa-company-search",
7891
7905
  "exa-people-search",
7892
7906
  "deep-research",
7893
- "system",
7894
7907
  "browser-agent",
7895
7908
  // Deprecated: 368 teams still have catalog rows in the integration table
7896
7909
  "browser-agent-devtools",
@@ -7920,7 +7933,6 @@ var INTEGRATION_SLUGS = [
7920
7933
  "sap-ecc",
7921
7934
  "msteams",
7922
7935
  "human-in-the-loop",
7923
- "email-attachments-reader",
7924
7936
  "document-processor",
7925
7937
  "google-docs",
7926
7938
  "google-sheets",
@@ -7995,6 +8007,20 @@ function isSupervisedModeIncompatibleIntegration(slug) {
7995
8007
  }
7996
8008
  __name(isSupervisedModeIncompatibleIntegration, "isSupervisedModeIncompatibleIntegration");
7997
8009
  __name2(isSupervisedModeIncompatibleIntegration, "isSupervisedModeIncompatibleIntegration");
8010
+ var RETIRED_INTEGRATION_SLUGS = [
8011
+ "system",
8012
+ "email-attachments-reader"
8013
+ ];
8014
+ var RETIRED_INTEGRATION_TYPES = RETIRED_INTEGRATION_SLUGS;
8015
+ function isRetiredIntegrationSlug(value) {
8016
+ return RETIRED_INTEGRATION_TYPES.includes(value);
8017
+ }
8018
+ __name(isRetiredIntegrationSlug, "isRetiredIntegrationSlug");
8019
+ __name2(isRetiredIntegrationSlug, "isRetiredIntegrationSlug");
8020
+ var IntegrationSlugRequestSchema = z24.enum([
8021
+ ...INTEGRATION_SLUGS,
8022
+ ...RETIRED_INTEGRATION_SLUGS
8023
+ ]);
7998
8024
  var SERVER_RESOLVED_MCP_URL_SLUGS = /* @__PURE__ */ new Set([
7999
8025
  "sap-ecc",
8000
8026
  "tableau",
@@ -8199,7 +8225,7 @@ var updateIntegrationRequestSchema = z24.object({
8199
8225
  });
8200
8226
  var testIntegrationRequestSchema = z24.object({
8201
8227
  server_url: z24.url().optional().describe("Raw MCP server URL to probe (for custom MCP servers)"),
8202
- integration_slug: IntegrationSlugSchema.optional().describe(
8228
+ integration_slug: IntegrationSlugRequestSchema.optional().describe(
8203
8229
  "Catalog integration slug. The backend resolves the MCP server URL and dummy auth headers server-side \u2014 no server_url or headers needed."
8204
8230
  ),
8205
8231
  headers: z24.record(z24.string(), z24.string()).optional().describe(
@@ -10900,8 +10926,10 @@ var AGENT_RUN_STATUSES = [
10900
10926
  // Finished successfully
10901
10927
  "failed",
10902
10928
  // Finished with error
10903
- "interrupted"
10929
+ "interrupted",
10904
10930
  // User interrupted
10931
+ "expired"
10932
+ // Waited for a human longer than the organization's data retention window
10905
10933
  ];
10906
10934
  var agentRunStatusSchema = z42.enum(AGENT_RUN_STATUSES);
10907
10935
  var AGENT_RUN_SOURCES = [
@@ -20129,13 +20157,16 @@ var AGENT_RUN_STATUSES2 = [
20129
20157
  // Finished successfully
20130
20158
  "failed",
20131
20159
  // Finished with error
20132
- "interrupted"
20160
+ "interrupted",
20133
20161
  // User interrupted
20162
+ "expired"
20163
+ // Waited for a human longer than the organization's data retention window
20134
20164
  ];
20135
20165
  var TERMINAL_AGENT_RUN_STATUSES = [
20136
20166
  "completed",
20137
20167
  "failed",
20138
- "interrupted"
20168
+ "interrupted",
20169
+ "expired"
20139
20170
  ];
20140
20171
  function isTerminalAgentRunStatus(status) {
20141
20172
  return TERMINAL_AGENT_RUN_STATUSES.includes(status);
@@ -21256,6 +21287,9 @@ var vpnConfigListResponseSchema = z192.object({
21256
21287
  var errorSchema2 = z202.object({
21257
21288
  error: z202.string()
21258
21289
  });
21290
+ var codedErrorSchema2 = errorSchema2.extend({
21291
+ code: z202.string().optional()
21292
+ });
21259
21293
  var CONFIG_FIELD_TYPES2 = [
21260
21294
  "freeText",
21261
21295
  "identifier",
@@ -22319,7 +22353,18 @@ function validateCompound(typeConfig, value) {
22319
22353
  return ok();
22320
22354
  }
22321
22355
  const partOptions = getRecordOption(typeConfig, "options");
22356
+ const optionalParts = getStringArrayOption(typeConfig, "optionalParts");
22357
+ const partNames = Object.keys(parts);
22358
+ const isGiven = /* @__PURE__ */ __name3((partName) => value[partName] !== void 0 && value[partName] !== null, "isGiven");
22359
+ if (optionalParts !== null && !partNames.some(isGiven)) {
22360
+ return fail(
22361
+ `Expected at least one of ${partNames.map((name) => `"${name}" (${String(parts[name])})`).join(" or ")}`
22362
+ );
22363
+ }
22322
22364
  for (const [partName, partType] of Object.entries(parts)) {
22365
+ if (optionalParts?.includes(partName) && !isGiven(partName)) {
22366
+ continue;
22367
+ }
22323
22368
  const result = validateScalar(
22324
22369
  partName,
22325
22370
  partType,
@@ -32278,15 +32323,6 @@ var INTEGRATION_DESCRIPTORS = [
32278
32323
  category: "duvo-platform",
32279
32324
  oauth_provider_id: null
32280
32325
  },
32281
- {
32282
- provider: "default",
32283
- type: "email-attachments-reader",
32284
- name: "Email Attachments Reader",
32285
- icon_url: "/integration-icons/email-attachments-reader.svg",
32286
- description: "Extract and process attachments from emails automatically.",
32287
- category: "duvo-platform",
32288
- oauth_provider_id: null
32289
- },
32290
32326
  {
32291
32327
  provider: "default",
32292
32328
  type: "document-processor",
@@ -32972,7 +33008,6 @@ var INTEGRATION_SLUGS2 = [
32972
33008
  "exa-company-search",
32973
33009
  "exa-people-search",
32974
33010
  "deep-research",
32975
- "system",
32976
33011
  "browser-agent",
32977
33012
  // Deprecated: 368 teams still have catalog rows in the integration table
32978
33013
  "browser-agent-devtools",
@@ -33002,7 +33037,6 @@ var INTEGRATION_SLUGS2 = [
33002
33037
  "sap-ecc",
33003
33038
  "msteams",
33004
33039
  "human-in-the-loop",
33005
- "email-attachments-reader",
33006
33040
  "document-processor",
33007
33041
  "google-docs",
33008
33042
  "google-sheets",
@@ -33078,9 +33112,22 @@ function isSupervisedModeIncompatibleIntegration2(slug) {
33078
33112
  }
33079
33113
  __name(isSupervisedModeIncompatibleIntegration2, "isSupervisedModeIncompatibleIntegration");
33080
33114
  __name3(isSupervisedModeIncompatibleIntegration2, "isSupervisedModeIncompatibleIntegration");
33115
+ var RETIRED_INTEGRATION_SLUGS2 = [
33116
+ "system",
33117
+ "email-attachments-reader"
33118
+ ];
33119
+ var RETIRED_INTEGRATION_TYPES2 = RETIRED_INTEGRATION_SLUGS2;
33120
+ function isRetiredIntegrationSlug2(value) {
33121
+ return RETIRED_INTEGRATION_TYPES2.includes(value);
33122
+ }
33123
+ __name(isRetiredIntegrationSlug2, "isRetiredIntegrationSlug");
33124
+ __name3(isRetiredIntegrationSlug2, "isRetiredIntegrationSlug");
33125
+ var IntegrationSlugRequestSchema2 = z442.enum([
33126
+ ...INTEGRATION_SLUGS2,
33127
+ ...RETIRED_INTEGRATION_SLUGS2
33128
+ ]);
33081
33129
  var DEPRECATED_INTEGRATION_SLUGS = /* @__PURE__ */ new Set([
33082
33130
  "browser-agent",
33083
- "email-attachments-reader",
33084
33131
  "exa",
33085
33132
  "firecrawl",
33086
33133
  "linear",
@@ -33290,7 +33337,7 @@ var updateIntegrationRequestSchema2 = z442.object({
33290
33337
  });
33291
33338
  var testIntegrationRequestSchema2 = z442.object({
33292
33339
  server_url: z442.url().optional().describe("Raw MCP server URL to probe (for custom MCP servers)"),
33293
- integration_slug: IntegrationSlugSchema2.optional().describe(
33340
+ integration_slug: IntegrationSlugRequestSchema2.optional().describe(
33294
33341
  "Catalog integration slug. The backend resolves the MCP server URL and dummy auth headers server-side \u2014 no server_url or headers needed."
33295
33342
  ),
33296
33343
  headers: z442.record(z442.string(), z442.string()).optional().describe(
@@ -34761,11 +34808,10 @@ var ORG_ROLE2 = {
34761
34808
  * A guest's reach is deliberately identical to a team member holding no
34762
34809
  * membership row at all, with two exceptions. The organization appears in
34763
34810
  * their switcher (`/organizations/mine` is self-scoped and ungated), where
34764
- * the row is rendered inert. Clarity landscape writes accept a guest whose
34765
- * team role allows them: placing their own team's work into the shared area
34766
- * structure, and adding areas as a team Manager
34767
- * (`ORG_ROLES_WITH_TEAM_MEMBERSHIP` in
34768
- * backend-node/src/modules/clarity-hierarchy/access.ts).
34811
+ * the row is rendered inert. Clarity landscape targets owned by one of their
34812
+ * teams are decided on the team role alone (`resolveLandscapeEditScope` in
34813
+ * backend-node/src/modules/clarity-hierarchy/access.ts), and the org reads in
34814
+ * {@link ORG_ROLES.TEAM_MEMBER_READS} include them.
34769
34815
  * See specs/org-owned-resources/PHASE_0_PLAN.md.
34770
34816
  */
34771
34817
  GUEST: "organization:guest"
@@ -34811,6 +34857,18 @@ var ORG_ROLES2 = {
34811
34857
  ORG_ROLE2.ADMIN,
34812
34858
  ORG_ROLE2.MEMBER
34813
34859
  ],
34860
+ /**
34861
+ * Org reads a team-only member (`GUEST`) needs to work in their teams: the
34862
+ * org logo in the switcher and the connection-sharing settings that decide
34863
+ * what their team's Connections pages offer. Org-wide data stays on `ALL`.
34864
+ */
34865
+ TEAM_MEMBER_READS: [
34866
+ ORG_ROLE2.EXECUTIVE,
34867
+ ORG_ROLE2.OWNER,
34868
+ ORG_ROLE2.ADMIN,
34869
+ ORG_ROLE2.MEMBER,
34870
+ ORG_ROLE2.GUEST
34871
+ ],
34814
34872
  // Org roles an SSO connection may auto-grant to every user it admits.
34815
34873
  // Excludes executive/owner: default_org_role is applied unconditionally to
34816
34874
  // every SSO login, so those tiers must never be broadly granted by config.
@@ -43082,6 +43140,36 @@ var applyAutomationRelocationResponseApiSchema = z1022.object({
43082
43140
  "What the relocation did. A retry of an applied plan returns the same result without relocating again."
43083
43141
  )
43084
43142
  });
43143
+ var mergeAutomationBodyApiSchema = z1022.object({
43144
+ into_automation_id: z1022.guid().describe(
43145
+ "The automation that survives the merge and receives this one's agents, queues, dashboards and declarations"
43146
+ )
43147
+ });
43148
+ var automationMergePreviewApiSchema = z1022.object({
43149
+ automation_id: z1022.guid().describe("The automation that will be retired"),
43150
+ into_automation_id: z1022.guid(),
43151
+ into_automation_name: z1022.string(),
43152
+ agents: z1022.array(automationRelocationMemberApiSchema).describe("Agents that move into the survivor"),
43153
+ queues: z1022.array(automationRelocationMemberApiSchema).describe("Queues that move into the survivor"),
43154
+ dashboard_count: z1022.int().describe(
43155
+ "Pulse dashboards that move into the survivor. A count rather than a list: some may be private to their author."
43156
+ ),
43157
+ trigger_count: z1022.int().describe("Trigger declarations that move with their agents"),
43158
+ schedule_count: z1022.int().describe("Schedule declarations that move with their agents"),
43159
+ policy_field_count: z1022.int().describe("Policy questions that move into the survivor's Policy"),
43160
+ process_identity: z1022.enum(["none", "survivor", "moves"]).describe(
43161
+ "Whose process identity the survivor keeps: none at all, its own, or the retired automation's, which moves to it. When both have one, the survivor keeps the one linked to a Clarity process, or its own when neither is"
43162
+ )
43163
+ });
43164
+ var previewAutomationMergeResponseApiSchema = z1022.object({
43165
+ merge: automationMergePreviewApiSchema
43166
+ });
43167
+ var mergeAutomationResponseApiSchema = z1022.object({
43168
+ merge: z1022.object({
43169
+ automation_id: z1022.guid().describe("The retired automation"),
43170
+ merged_into_id: z1022.guid().describe("The survivor")
43171
+ })
43172
+ });
43085
43173
  var BROWSER_EGRESS_MODES2 = [
43086
43174
  "provider",
43087
43175
  "direct",
@@ -44826,6 +44914,11 @@ var FEATURE_FLAG_CATALOGUE = {
44826
44914
  defaultValue: false,
44827
44915
  description: "Exposes the Automations workspace migration entry point, together with AUTOMATION_MIGRATION \u2014 the entry point needs both flags on, and this flag alone no longer shows it. When both are on, team Administrators and Owners see a 'Migrate to automations' action on the Agents page that sorts the team's standalone agents and queues into Automations (one per connected closure, via the same closure logic as the cross-team move) and stamps team.automations_migrated_at. Which conversion runs is decided by AUTOMATION_VERSIONING, not by this flag: off (the default) writes automation containers and identity FKs only, so builds, triggers and schedules keep their existing lifecycle; on, the same entry point converts straight to revision semantics and additionally stamps team.automation_revisions_migrated_at. Neither conversion can be reversed: nothing clears team.automations_migrated_at. Revision semantics are also reachable for an already-converted team through the separate phase-2 cutover. The workspace stamp \u2014 not this flag \u2014 drives the Automations sidebar entry, and a team carrying it keeps Automations access whatever this flag says: the team gate `isAutomationsEnabledForTeam`, and Modules through it, treat a migrated team as enabled, because its agents live inside automations and nothing undoes the migration, so this flag only decides access for teams that have not migrated. The one exception is the `policy-apply` chat preset, which still reads this flag directly. The migration gate resolves it for the caller, user-level overrides included, as the button does. Off-by-default for staged, per-team rollout."
44828
44916
  },
44917
+ CRON_FREE_PRO_AUTOMATIONS: {
44918
+ key: "cron_free_pro_automations",
44919
+ defaultValue: false,
44920
+ description: "Kill switch for the background conversion of Free and Pro teams to the lite Automations workspace. When on, the `cron-automation-auto-migration` Cloud Scheduler job (POST /v1/cron/automation-auto-migration, every 5 minutes) picks a small batch of teams with an active or trialing subscription on the Free or Pro plan that are not yet on the workspace (org-managed teams \u2014 enterprise billing, following or inheriting organization grants, or holding a sales_override grant \u2014 are left to the manual conversion), and runs the same lite conversion as POST /automations/migrate for each: agents and queues are sorted into automation containers and team.automations_migrated_at is stamped, while builds, triggers and schedules keep their legacy lifecycle. It never converts to revision semantics, and the conversion cannot be reversed. When off (the default), every tick is a no-op. The job itself ships paused in Terraform, so nothing ticks until it is unpaused there as well \u2014 this flag alone converts nothing. Resolved globally only \u2014 the cron has no user, team or organization to resolve against, so only the global override counts; a team, organization or user override has no effect. Independent of AUTOMATIONS and AUTOMATION_MIGRATION, which gate the manual entry point."
44921
+ },
44829
44922
  AUTOMATION_MIGRATION: {
44830
44923
  key: "automation_migration",
44831
44924
  defaultValue: false,
@@ -44834,7 +44927,7 @@ var FEATURE_FLAG_CATALOGUE = {
44834
44927
  AUTOMATIONS_ON_NEW_TEAMS: {
44835
44928
  key: "automations_on_new_teams",
44836
44929
  defaultValue: false,
44837
- description: "Starts every newly created team on the lite Automations workspace: the team row is inserted with team.automations_migrated_at already stamped, which is all the lite conversion would write for a team with no agents or queues yet. Applies to both team creators \u2014 backend-node's createTeam (signup, onboarding, organization team create, Clarity provisioning, legacy admin) and the standalone admin's organization team create \u2014 on any plan. Resolved for the team's owner in the organization the team is created in, with no team (there is none yet): user, organization and global overrides decide it, so it can be enabled for one owner, one organization or everyone. The owner is the user creating the team in the product, and the owner an admin assigns in the admin app. Read once at creation; turning the flag off later does not move a team that already started on the workspace, since nothing clears the stamp. Independent of AUTOMATION_AUTO_MIGRATION, which converts existing teams. Off by default: new teams start standalone until it is enabled."
44930
+ description: "Starts every newly created team on the lite Automations workspace: the team row is inserted with team.automations_migrated_at already stamped, which is all the lite conversion would write for a team with no agents or queues yet. Applies to both team creators \u2014 backend-node's createTeam (signup, onboarding, organization team create, Clarity provisioning, legacy admin) and the standalone admin's organization team create \u2014 on any plan. Resolved for the team's owner in the organization the team is created in, with no team (there is none yet): user, organization and global overrides decide it, so it can be enabled for one owner, one organization or everyone. The owner is the user creating the team in the product, and the owner an admin assigns in the admin app. Read once at creation; turning the flag off later does not move a team that already started on the workspace, since nothing clears the stamp. Independent of CRON_FREE_PRO_AUTOMATIONS, which converts existing teams. Off by default: new teams start standalone until it is enabled."
44838
44931
  },
44839
44932
  AUTOMATION_VERSIONING: {
44840
44933
  key: "automation_versioning",
@@ -45026,7 +45119,7 @@ var FEATURE_FLAG_CATALOGUE = {
45026
45119
  DECISION_MODEL_BROWSING: {
45027
45120
  key: "decision_model_browsing",
45028
45121
  defaultValue: false,
45029
- description: "Team/org opt-in (user-level overrides are ignored) for the `browse` tool on the Enterprise Browser (formerly `jev_browsing`; its rows were copied to this key, and the old ones are inert). When on, backend-node stamps `x-duvo-decision-model-browsing-enabled: true` on every internal MCP server it builds, and mcp-server registers `browse` (it hands a short browser sub-goal to TypeSafe's Jev model, with a one-step Claude fallback when the model is unsure); the browser prompt makes browse the default way to operate the browser. mcp-server trusts the header only from a KMS-decrypted envelope, and registers the tool only when it also holds a TYPESAFE_API_KEY, which preview, staging and production mcp-servers have (dev does not). Staging and previews share a database, so a team flagged there gets browse on both. The browse prompt section is added wherever the flag is on, and is worded to apply only when the tool is available. Page text and field values (never passwords: saved logins and {secret} values are typed only into password fields and shown to Jev as placeholders) are sent to TypeSafe (US-hosted), so enable only for teams whose data may leave Duvo's infrastructure. The flag gates new runs, not live traffic: the opt-in header is stamped into a run's MCP config at dispatch, so runs already in flight keep `browse` after the flag is cleared (and the fallback route honours a cleared flag only after a cache of up to 30 seconds). To stop live traffic to TypeSafe, remove TYPESAFE_API_KEY from mcp-server. Off by default."
45122
+ description: "Team/org opt-in for the `browse` tool on the Enterprise Browser; user overrides are ignored. Browse sends page text and field values (never passwords) to TypeSafe in the US, so enable it only for teams whose data may leave Duvo. Applies to new runs; to stop all TypeSafe traffic, remove TYPESAFE_API_KEY from mcp-server. Staging and previews share these flags."
45030
45123
  },
45031
45124
  JIRA_CONNECTION: {
45032
45125
  key: "jira_connection",
@@ -46587,6 +46680,19 @@ var RedactedMcpServerConfigSchema = z135.object({
46587
46680
  integrationSlug: IntegrationSlugSchema2,
46588
46681
  instanceId: z135.string().nullable()
46589
46682
  });
46683
+ function isRetiredMcpServer(server) {
46684
+ return typeof server === "object" && server !== null && "integrationSlug" in server && typeof server.integrationSlug === "string" && RETIRED_INTEGRATION_TYPES2.includes(server.integrationSlug);
46685
+ }
46686
+ __name(isRetiredMcpServer, "isRetiredMcpServer");
46687
+ __name3(isRetiredMcpServer, "isRetiredMcpServer");
46688
+ var FrozenMcpServersSchema = z135.preprocess(
46689
+ (servers) => typeof servers === "object" && servers !== null && !Array.isArray(servers) ? Object.fromEntries(
46690
+ Object.entries(servers).filter(
46691
+ ([, server]) => !isRetiredMcpServer(server)
46692
+ )
46693
+ ) : servers,
46694
+ z135.record(z135.string(), RedactedMcpServerConfigSchema)
46695
+ );
46590
46696
  var ThreadContextMessageSchema = z135.object({
46591
46697
  role: z135.enum(["user", "assistant"]),
46592
46698
  content: z135.string()
@@ -46614,7 +46720,7 @@ var RunConfigSchema = AgentConfigSchema2.omit({
46614
46720
  subAgents: true
46615
46721
  }).extend({
46616
46722
  task: z135.string(),
46617
- mcpServers: z135.record(z135.string(), RedactedMcpServerConfigSchema),
46723
+ mcpServers: FrozenMcpServersSchema,
46618
46724
  /**
46619
46725
  * Newly created runs store `ResolvedSkillRef` entries exclusively. The
46620
46726
  * `string` arm exists ONLY for stored v1 rows written before skill pinning
@@ -46673,7 +46779,7 @@ var ResolvedPolicyValueSchema = z135.object({
46673
46779
  value: policyValueJsonSchema
46674
46780
  });
46675
46781
  var CodeStepRunConfigSchema = codeStepConfigSchema2.extend({
46676
- mcpServers: z135.record(z135.string(), RedactedMcpServerConfigSchema).default({}).describe(
46782
+ mcpServers: FrozenMcpServersSchema.default({}).describe(
46677
46783
  "Redacted MCP server configs resolved for this run. Keyed as `<slug>` or `<slug>_<instanceId>`. Secrets resolved at call time."
46678
46784
  ),
46679
46785
  /**
@@ -51747,7 +51853,7 @@ __name(collect4, "collect");
51747
51853
  function registerAutomationsAddConfigField(automations) {
51748
51854
  automations.command("add-config-field <id>").description(
51749
51855
  "Add an optional Policy question to an automation; its key is derived from the label"
51750
- ).requiredOption("--label <label>", "short name shown as the row heading").requiredOption("--question <text>", "the question asked during setup").requiredOption(
51856
+ ).requiredOption("--label <label>", "short name shown as the row heading").requiredOption("--question <text>", "the question asked during setup").option("--guidance <text>", "what to fill in, in plain sentences").option("--example <text>", "an example answer").requiredOption(
51751
51857
  "--type <type>",
51752
51858
  `answer type: ${CUSTOM_CONFIG_FIELD_TYPES.join(", ")}`
51753
51859
  ).option("--section <name>", "section to add the field to").option(
@@ -51772,6 +51878,8 @@ function registerAutomationsAddConfigField(automations) {
51772
51878
  const body = addConfigFieldBodySchema.safeParse({
51773
51879
  label: options.label,
51774
51880
  question: options.question,
51881
+ guidance: options.guidance,
51882
+ example: options.example,
51775
51883
  type: options.type,
51776
51884
  section: options.section,
51777
51885
  fill_mode: options.fillMode,
@@ -52306,20 +52414,20 @@ function collect5(value, previous = []) {
52306
52414
  return [...previous, value];
52307
52415
  }
52308
52416
  __name(collect5, "collect");
52309
- function resolveSection(options) {
52310
- if (options.section !== void 0 && options.clearSection) {
52311
- throw new CliError("Pass either --section or --clear-section, not both.");
52417
+ function resolveClearable(value, clear, flag) {
52418
+ if (value !== void 0 && clear) {
52419
+ throw new CliError(`Pass either --${flag} or --clear-${flag}, not both.`);
52312
52420
  }
52313
- return options.clearSection ? null : options.section;
52421
+ return clear ? null : value;
52314
52422
  }
52315
- __name(resolveSection, "resolveSection");
52423
+ __name(resolveClearable, "resolveClearable");
52316
52424
  function registerAutomationsEditConfigField(automations) {
52317
52425
  automations.command("edit-config-field <id> <fieldId>").description(
52318
- "Edit a Policy question's definition (label, question, section, options, columns, type); its answer is kept"
52426
+ "Edit a Policy question's definition (label, question, guidance, example, section, options, columns, type); its answer is kept"
52319
52427
  ).option("--label <label>", "new short name shown as the row heading").option(
52320
52428
  "--question <text>",
52321
52429
  "new question; a reworded one gets a new version"
52322
- ).option("--section <name>", "move the field to the end of this section").option("--clear-section", "move the field out of any section").option(
52430
+ ).option("--guidance <text>", "new guidance on what to fill in").option("--clear-guidance", "remove the guidance").option("--example <text>", "new example answer").option("--clear-example", "remove the example answer").option("--section <name>", "move the field to the end of this section").option("--clear-section", "move the field out of any section").option(
52323
52431
  "--option <value>",
52324
52432
  "a choice of a select or multiSelect field; replaces the whole list (repeat for each)",
52325
52433
  collect5,
@@ -52336,7 +52444,21 @@ function registerAutomationsEditConfigField(automations) {
52336
52444
  const body = updateConfigFieldDefinitionBodySchema.safeParse({
52337
52445
  label: options.label,
52338
52446
  question: options.question,
52339
- section: resolveSection(options),
52447
+ guidance: resolveClearable(
52448
+ options.guidance,
52449
+ options.clearGuidance,
52450
+ "guidance"
52451
+ ),
52452
+ example: resolveClearable(
52453
+ options.example,
52454
+ options.clearExample,
52455
+ "example"
52456
+ ),
52457
+ section: resolveClearable(
52458
+ options.section,
52459
+ options.clearSection,
52460
+ "section"
52461
+ ),
52340
52462
  options: options.option.length > 0 ? options.option : void 0,
52341
52463
  columns: options.column.length > 0 ? options.column : void 0,
52342
52464
  type: options.type
@@ -78314,7 +78436,7 @@ __name(isStandaloneBinary, "isStandaloneBinary");
78314
78436
  import { createRequire } from "node:module";
78315
78437
  function getPackageMeta() {
78316
78438
  if (true) {
78317
- return { name: "@duvoai/cli", version: "1.91.0" };
78439
+ return { name: "@duvoai/cli", version: "1.92.0" };
78318
78440
  }
78319
78441
  const require2 = createRequire(import.meta.url);
78320
78442
  const raw = require2("../../package.json");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duvoai/cli",
3
- "version": "1.91.0",
3
+ "version": "1.92.0",
4
4
  "description": "Command-line interface for the Duvo public API",
5
5
  "bin": {
6
6
  "duvo": "./dist/bin/duvo.js"