@tenonhq/dovetail-servicenow 0.0.48 → 0.0.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1151,7 +1151,18 @@ console.log(formatLayoutResult("form layout", result));
1151
1151
  `dove-sn mcp` runs a self-contained MCP stdio server exposing the tools to
1152
1152
  Claude Code and agents: `create_view`, `set_list_layout`, `set_form_layout`,
1153
1153
  `set_related_lists`, `add_choices_to_field`, the schema verbs `create_table` /
1154
- `add_column` / `add_index` (a single-column unique index via `sys_dictionary.unique`,
1154
+ `add_column` (same-scope by default; `crossScope: true` with `scope` adds a column OWNED
1155
+ by another app — a Journey field on an Automate table, element `<scope>_<name>` —
1156
+ guarded by `sys_db_object.alter_access` and the update set's scope, with the stored
1157
+ element and `sys_scope` read back; the CLI flag is `--cross-scope`. A cross-scope
1158
+ result also carries `designAccess`: the platform UI refuses cross-scope authoring
1159
+ without a `sys_scope_design_access` record (column scope -> table scope), so a missing
1160
+ one is FLAGGED — not blocking, the headless insert works without it — and
1161
+ `ensureDesignAccess: true` / `--ensure-design-access` creates it first in the same
1162
+ update set) / `design_access` (ensure that record on its own — idempotent, created in
1163
+ the SOURCE app's update set and read back; CLI `dove-sn design-access --source <app>
1164
+ --target <app> --update-set <sys_id>`, `--dry-run` only reports exists/missing) /
1165
+ `add_index` (a single-column unique index via `sys_dictionary.unique`,
1155
1166
  read back from the `v_db_index` view - uniqueness enforcement is always reported
1156
1167
  unverified) / `index_list` (read-only: a table's database indexes from `v_db_index`,
1157
1168
  the only index read surface - `sys_index` is API-level-ACL 403 and `sys_index_column`
package/dist/cli.js CHANGED
@@ -1340,14 +1340,60 @@ async function runCreateTable(flags) {
1340
1340
  return 2;
1341
1341
  return 0;
1342
1342
  }
1343
+ /**
1344
+ * dove-sn design-access:
1345
+ * --source x_cadso_journey --target x_cadso_automate
1346
+ * [--update-set <sys_id in source>] [--dry-run] [--json]
1347
+ * Ensures the sys_scope_design_access record that lets the SOURCE app author in the
1348
+ * TARGET app's tables (required by the platform UI for cross-scope columns). Idempotent;
1349
+ * --dry-run only reports whether it exists. --update-set is required unless --dry-run.
1350
+ */
1351
+ async function runDesignAccess(flags) {
1352
+ if (!flags.source || !flags.target) {
1353
+ process.stderr.write("design-access: --source (authoring app) and --target (app that owns the tables) are required\n");
1354
+ return 1;
1355
+ }
1356
+ var isDryRun = flags["dry-run"] === "true";
1357
+ if (!isDryRun && !flags["update-set"]) {
1358
+ process.stderr.write("design-access: --update-set (an update set in --source) is required on the live path (only --dry-run works without one)\n");
1359
+ return 1;
1360
+ }
1361
+ var result = await (0, table_1.ensureDesignAccess)({
1362
+ client: (0, client_1.createClient)({}),
1363
+ sourceScope: flags.source,
1364
+ targetScope: flags.target,
1365
+ updateSetSysId: flags["update-set"],
1366
+ dryRun: isDryRun,
1367
+ });
1368
+ if (flags.json === "true") {
1369
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
1370
+ }
1371
+ else {
1372
+ process.stdout.write("[" +
1373
+ result.status +
1374
+ "] " +
1375
+ result.sourceScope +
1376
+ " -> " +
1377
+ result.targetScope +
1378
+ (result.sysId ? " (" + result.sysId + ")" : "") +
1379
+ "\n" +
1380
+ result.note +
1381
+ "\n");
1382
+ }
1383
+ if (result.status === "failed")
1384
+ return 2;
1385
+ return 0;
1386
+ }
1343
1387
  /**
1344
1388
  * dove-sn add-column:
1345
1389
  * --table x_cadso_journey --label URL --type url
1346
1390
  * [--name url] [--max-length 1024] [--reference <table>]
1347
1391
  * [--mandatory] [--default <value>] [--dependent-on-field <element>]
1348
- * [--scope x_cadso_journey] [--update-set <sys_id>]
1349
- * [--from-json <spec.json>] [--dry-run] [--debug] [--json]
1350
- * --update-set is required unless --dry-run.
1392
+ * [--scope x_cadso_journey] [--cross-scope] [--ensure-design-access]
1393
+ * [--update-set <sys_id>] [--from-json <spec.json>] [--dry-run] [--debug] [--json]
1394
+ * --update-set is required unless --dry-run. --cross-scope opts in to a column
1395
+ * OWNED by --scope when that differs from the table's scope; a missing Design Access
1396
+ * record is flagged, and --ensure-design-access creates it first.
1351
1397
  */
1352
1398
  async function runAddColumn(flags) {
1353
1399
  var spec = {};
@@ -1384,6 +1430,13 @@ async function runAddColumn(flags) {
1384
1430
  var scope = flags.scope || spec.scope;
1385
1431
  if (scope)
1386
1432
  params.scope = scope;
1433
+ if (flags["cross-scope"] === "true" || spec.crossScope === true) {
1434
+ params.crossScope = true;
1435
+ }
1436
+ if (flags["ensure-design-access"] === "true" ||
1437
+ spec.ensureDesignAccess === true) {
1438
+ params.ensureDesignAccess = true;
1439
+ }
1387
1440
  var us = flags["update-set"] || spec.updateSetSysId;
1388
1441
  if (us)
1389
1442
  params.updateSetSysId = us;
@@ -2612,6 +2665,9 @@ async function dispatch(parsed) {
2612
2665
  if (parsed.command === "add-column") {
2613
2666
  return await runAddColumn(parsed.flags);
2614
2667
  }
2668
+ if (parsed.command === "design-access") {
2669
+ return await runDesignAccess(parsed.flags);
2670
+ }
2615
2671
  if (parsed.command === "add-index") {
2616
2672
  return await runAddIndex(parsed.flags);
2617
2673
  }
package/dist/cliUsage.js CHANGED
@@ -200,7 +200,19 @@ exports.VERB_USAGE = {
200
200
  value: "<element>",
201
201
  note: "Sibling column a document_id resolves against (its table_name column); must already exist.",
202
202
  },
203
- { flag: "scope", value: "<x_scope>", note: "Owning app scope when it cannot be inferred from the table." },
203
+ {
204
+ flag: "scope",
205
+ value: "<x_scope>",
206
+ note: "Owning app scope. Must match the table's scope unless --cross-scope is passed.",
207
+ },
208
+ {
209
+ flag: "cross-scope",
210
+ note: "Opt in to a column OWNED by --scope, which differs from the table's scope (element becomes <scope>_<name>; the table must allow new fields; --update-set must be in --scope).",
211
+ },
212
+ {
213
+ flag: "ensure-design-access",
214
+ note: "Cross-scope only: create the missing sys_scope_design_access record (--scope -> table's scope) first, in the same update set. Without it a missing record is only flagged.",
215
+ },
204
216
  { flag: "update-set", value: "<sys_id>", note: "REQUIRED on the live path (only --dry-run works without one)." },
205
217
  DRY_RUN_FLAG,
206
218
  DEBUG_FLAG,
@@ -209,7 +221,30 @@ exports.VERB_USAGE = {
209
221
  gate: "dry-run-flag",
210
222
  gateNote: "--update-set is required unless --dry-run.",
211
223
  example: "dove-sn add-column --table x_cadso_journey --label URL --type url --max-length 1024 --update-set <sys_id>",
212
- notes: ["Exit 2 when the write landed but the read-back does not show the column."],
224
+ notes: [
225
+ "Exit 2 when the write landed but the read-back does not show the column.",
226
+ "Cross-scope: dove-sn add-column --table x_cadso_automate_email_batch --label 'Instance Step' --name instance_step --type reference --reference x_cadso_journey_instance_step --scope x_cadso_journey --cross-scope --update-set <journey set>",
227
+ ],
228
+ },
229
+ "design-access": {
230
+ summary: "Ensure the sys_scope_design_access record that lets one app author in another app's tables (required by the UI for cross-scope columns)",
231
+ required: [
232
+ { flag: "source", value: "<x_scope>", note: "The AUTHORING app (owns the record and its update set)." },
233
+ { flag: "target", value: "<x_scope>", note: "The app that OWNS the tables." },
234
+ ],
235
+ optional: [
236
+ { flag: "update-set", value: "<sys_id>", note: "Update set in --source. REQUIRED on the live path." },
237
+ DRY_RUN_FLAG,
238
+ JSON_FLAG,
239
+ ],
240
+ gate: "dry-run-flag",
241
+ gateNote: "--update-set is required unless --dry-run; --dry-run reports exists/missing only.",
242
+ example: "dove-sn design-access --source x_cadso_journey --target x_cadso_automate --update-set <journey set>",
243
+ notes: [
244
+ "Idempotent: an existing record is reported and nothing is written.",
245
+ "Symptom it fixes: \"Invalid 'Table' selected on the Dictionary Entry record ... can only select '<app>' tables with read access enabled\" — the table's own access flags are NOT the gate.",
246
+ "Exit 2 when the record could not be created or verified on read-back.",
247
+ ],
213
248
  },
214
249
  "set-column": {
215
250
  summary: "Update an EXISTING column's schema (label/mandatory/default/read-only/max-length/dependent-on-field), then verify",
package/dist/index.d.ts CHANGED
@@ -21,8 +21,8 @@ export { sincPlugin } from "./plugin";
21
21
  export { listTemplates, verifyArtifact, cloneSubflow, cloneActionType, resolveScope, slugInternalName, fetchActionSteps, findStepInput, triggerPublication, publishActionType, editActionType, defineActionType, planActionDefinition, validateDefineSpec, applyStepOps, verifySteps, summarizeSteps, formatStepPill, readFlow, readActionType, publishFlow, copyFlow, createFlow, buildPublishModel, editFlow, testFlow, DEFAULT_RUN_FLOW_PATH, generateSysId, topoSort, executeWritePlan, WriteOrderError, } from "./flowDesigner";
22
22
  export type { TemplateRef, ListTemplatesParams, FlowKind, VerifyExpect, VerifyFound, VerifyFailure, VerifyReport, VerifyArtifactParams, CloneSubflowParams, CloneSubflowResult, CloneActionTypeParams, CloneActionTypeResult, CloneActionTypePlan, CloneActionTypeStepReport, TriggerPublicationParams, TriggerPublicationResult, PublishActionTypeParams, PublishActionTypeResult, EditActionTypeParams, EditActionTypeResult, EditActionTypeOps, DefineActionSpec, DefineActionInputSpec, DefineActionOutputSpec, DefineStepSpec, DefineStepVarSpec, DefineStepOutputSpec, DefineStepValue, DefineActionTypeParams, DefineActionTypeResult, DefineActionDiff, DefineActionPlan, PlanActionDefinitionParams, ActionView, StepView, StepOps, StepRecord, StepSummary, StepIoSummary, PatchStepScriptOp, SetStepInputOp, AddStepOutputOp, AddStepInputOp, ApplyStepOpsResult, VerifyStepsResult, ReadFlowParams, ReadFlowResult, FlowStep, FlowVariable, ReadActionTypeParams, ReadActionTypeResult, ActionIo, PublishFlowParams, PublishFlowResult, CopyFlowParams, CopyFlowResult, CreateFlowParams, CreateFlowResult, EditFlowParams, EditFlowResult, EditFlowOps, StepInputPatch, TestFlowParams, TestFlowResult, WriteOp, WriteOpResult, } from "./flowDesigner";
23
23
  export type { ServiceNowClientConfig, ChoiceValue, ChoiceType, AddChoicesParams, AddChoicesResult, ChoiceActionResult, ChoiceFieldRef, RemoveChoicesParams, RemoveChoicesResult, ChoiceRemovalResult, DictionaryRecord, UpdateSetRecord, LayoutAction, LayoutRecordResult, LayoutResult, CreateViewParams, CreateViewResult, FormSectionSpec, SetFormLayoutParams, SetListLayoutParams, SetRelatedListsParams, ChunkRole, ChunkInfo, ChunkResult, PrunedResult, HostAssetsParams, HostAssetsResult, } from "./types";
24
- export { createTable, projectTableGraph, buildColumnXml, normalizeColumns, resolveType, applyTableSaveOverlay, defaultAccessFlags, TYPE_MAP, DEFAULT_SUPER_CLASS, DEFAULT_SAVE_ACTION, addColumn, deriveElement, addIndex, parseIndexColumns, indexMatchesColumns, listIndexes, createIndex, validateCreateIndex, DEFAULT_INDEX_FORM_PATH, NOT_IN_UPDATE_SET, setColumn, resolveAttributes, toStoredValue, setTable, resolveTableAttributes, } from "./table";
25
- export type { CreateTableParams, CreateTableResult, AddColumnParams, AddColumnResult, AddIndexParams, AddIndexResult, AddIndexVerification, ListIndexesParams, ListIndexesResult, TableIndex, CreateIndexParams, CreateIndexResult, SetColumnParams, SetColumnResult, ColumnAttributes, AttributeChange, SetTableParams, SetTableResult, TableAttributes, TableGraph, NormalizedColumn, ColumnSpec, AccessFlags, OverlaySpec, } from "./table";
24
+ export { createTable, projectTableGraph, buildColumnXml, normalizeColumns, resolveType, applyTableSaveOverlay, defaultAccessFlags, TYPE_MAP, DEFAULT_SUPER_CLASS, DEFAULT_SAVE_ACTION, addColumn, deriveElement, ensureDesignAccess, findDesignAccess, DESIGN_ACCESS_TABLE, addIndex, parseIndexColumns, indexMatchesColumns, listIndexes, createIndex, validateCreateIndex, DEFAULT_INDEX_FORM_PATH, NOT_IN_UPDATE_SET, setColumn, resolveAttributes, toStoredValue, setTable, resolveTableAttributes, } from "./table";
25
+ export type { CreateTableParams, CreateTableResult, AddColumnParams, AddColumnResult, DesignAccessFlag, EnsureDesignAccessParams, EnsureDesignAccessResult, AddIndexParams, AddIndexResult, AddIndexVerification, ListIndexesParams, ListIndexesResult, TableIndex, CreateIndexParams, CreateIndexResult, SetColumnParams, SetColumnResult, ColumnAttributes, AttributeChange, SetTableParams, SetTableResult, TableAttributes, TableGraph, NormalizedColumn, ColumnSpec, AccessFlags, OverlaySpec, } from "./table";
26
26
  export { setField } from "./setField";
27
27
  export { createRecord } from "./createRecord";
28
28
  export type { RecordWriteResult, SetFieldParams, SetFieldResult, } from "./setField";
package/dist/index.js CHANGED
@@ -7,8 +7,8 @@
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.editFlow = exports.buildPublishModel = exports.createFlow = exports.copyFlow = exports.publishFlow = exports.readActionType = exports.readFlow = exports.formatStepPill = exports.summarizeSteps = exports.verifySteps = exports.applyStepOps = exports.validateDefineSpec = exports.planActionDefinition = exports.defineActionType = exports.editActionType = exports.publishActionType = exports.triggerPublication = exports.findStepInput = exports.fetchActionSteps = exports.slugInternalName = exports.resolveScope = exports.cloneActionType = exports.cloneSubflow = exports.verifyArtifact = exports.listTemplates = exports.sincPlugin = exports.formatCreateViewResult = exports.formatLayoutResult = exports.setRelatedLists = exports.setFormLayout = exports.setListLayout = exports.createView = exports.formatRemoveChoicesResult = exports.formatAddChoicesResult = exports.formatHostAssetsResult = exports.classifyChunks = exports.hostAssets = exports.ChoiceWriteError = exports.removeChoicesFromField = exports.addChoicesToField = exports.resolveConfigFromEnvFile = exports.createClientFromEnvFile = exports.assertWriteAllowed = exports.evaluateWriteGate = exports.isCiEnvironment = exports.resolveExecutionContext = exports.PROCESSFLOW_PATH_PREFIX = exports.isProcessflowPath = exports.resolveFlowAuth = exports.createClient = void 0;
10
- exports.parseProgressTree = exports.parseXmlAnswer = exports.PUBLISH_TARGETS = exports.parsePublishTargets = exports.describeTarget = exports.resolveUpdateSetNaming = exports.buildPublishToUpdateSetFields = exports.buildCreateUpdateSetFields = exports.buildStartFields = exports.publishApp = exports.INVOKE_REST_METHODS = exports.invokeRest = exports.verifyReadBack = exports.readBackMatches = exports.hasHtmlEntity = exports.decodeHtmlEntities = exports.deleteRecord = exports.createRecord = exports.setField = exports.resolveTableAttributes = exports.setTable = exports.toStoredValue = exports.resolveAttributes = exports.setColumn = exports.NOT_IN_UPDATE_SET = exports.DEFAULT_INDEX_FORM_PATH = exports.validateCreateIndex = exports.createIndex = exports.listIndexes = exports.indexMatchesColumns = exports.parseIndexColumns = exports.addIndex = exports.deriveElement = exports.addColumn = exports.DEFAULT_SAVE_ACTION = exports.DEFAULT_SUPER_CLASS = exports.TYPE_MAP = exports.defaultAccessFlags = exports.applyTableSaveOverlay = exports.resolveType = exports.normalizeColumns = exports.buildColumnXml = exports.projectTableGraph = exports.createTable = exports.WriteOrderError = exports.executeWritePlan = exports.topoSort = exports.generateSysId = exports.DEFAULT_RUN_FLOW_PATH = exports.testFlow = void 0;
11
- exports.SECRET_INTERNAL_TYPES = exports.SENTINEL = exports.isCapturable = exports.secretFieldsFromDictionary = exports.mergeSecretRules = exports.loadSecretRules = exports.defaultSecretRules = exports.escapeRegExp = exports.encodeXmlText = exports.encodeXmlEntities = exports.stripJsonValue = exports.stripField = exports.plannedStrips = exports.recordFieldNames = exports.readRecordTable = exports.readField = exports.verifyStripped = exports.stripSecrets = exports.DEFAULT_EXPORT_APP_TIMEOUT_MS = exports.buildPublishFields = exports.buildCreateSetFields = exports.exportApp = exports.DEFAULT_MAX_ROWS = exports.MAX_PAGE_SIZE = exports.DEFAULT_PAGE_SIZE = exports.UPDATE_XML_FIELDS = exports.xmlEscape = exports.formatUnloadDate = exports.parseStatsCount = exports.refreshTypeFields = exports.fetchUpdateXmlRows = exports.countUpdateXml = exports.countUnloadRecords = exports.renderUnload = exports.renderRemoteUpdateSet = exports.renderUpdateXmlRow = exports.exportUpdateSet = exports.PUBLISH_POLL_DELAYS_MS = exports.DEFAULT_PUBLISH_TIMEOUT_MS = exports.parseCicdProgress = exports.parseCicdPublishResponse = exports.harvestProgressResults = exports.flattenSteps = exports.classifyProgress = void 0;
10
+ exports.parsePublishTargets = exports.describeTarget = exports.resolveUpdateSetNaming = exports.buildPublishToUpdateSetFields = exports.buildCreateUpdateSetFields = exports.buildStartFields = exports.publishApp = exports.INVOKE_REST_METHODS = exports.invokeRest = exports.verifyReadBack = exports.readBackMatches = exports.hasHtmlEntity = exports.decodeHtmlEntities = exports.deleteRecord = exports.createRecord = exports.setField = exports.resolveTableAttributes = exports.setTable = exports.toStoredValue = exports.resolveAttributes = exports.setColumn = exports.NOT_IN_UPDATE_SET = exports.DEFAULT_INDEX_FORM_PATH = exports.validateCreateIndex = exports.createIndex = exports.listIndexes = exports.indexMatchesColumns = exports.parseIndexColumns = exports.addIndex = exports.DESIGN_ACCESS_TABLE = exports.findDesignAccess = exports.ensureDesignAccess = exports.deriveElement = exports.addColumn = exports.DEFAULT_SAVE_ACTION = exports.DEFAULT_SUPER_CLASS = exports.TYPE_MAP = exports.defaultAccessFlags = exports.applyTableSaveOverlay = exports.resolveType = exports.normalizeColumns = exports.buildColumnXml = exports.projectTableGraph = exports.createTable = exports.WriteOrderError = exports.executeWritePlan = exports.topoSort = exports.generateSysId = exports.DEFAULT_RUN_FLOW_PATH = exports.testFlow = void 0;
11
+ exports.SECRET_INTERNAL_TYPES = exports.SENTINEL = exports.isCapturable = exports.secretFieldsFromDictionary = exports.mergeSecretRules = exports.loadSecretRules = exports.defaultSecretRules = exports.escapeRegExp = exports.encodeXmlText = exports.encodeXmlEntities = exports.stripJsonValue = exports.stripField = exports.plannedStrips = exports.recordFieldNames = exports.readRecordTable = exports.readField = exports.verifyStripped = exports.stripSecrets = exports.DEFAULT_EXPORT_APP_TIMEOUT_MS = exports.buildPublishFields = exports.buildCreateSetFields = exports.exportApp = exports.DEFAULT_MAX_ROWS = exports.MAX_PAGE_SIZE = exports.DEFAULT_PAGE_SIZE = exports.UPDATE_XML_FIELDS = exports.xmlEscape = exports.formatUnloadDate = exports.parseStatsCount = exports.refreshTypeFields = exports.fetchUpdateXmlRows = exports.countUpdateXml = exports.countUnloadRecords = exports.renderUnload = exports.renderRemoteUpdateSet = exports.renderUpdateXmlRow = exports.exportUpdateSet = exports.PUBLISH_POLL_DELAYS_MS = exports.DEFAULT_PUBLISH_TIMEOUT_MS = exports.parseCicdProgress = exports.parseCicdPublishResponse = exports.harvestProgressResults = exports.flattenSteps = exports.classifyProgress = exports.parseProgressTree = exports.parseXmlAnswer = exports.PUBLISH_TARGETS = void 0;
12
12
  var client_1 = require("./client");
13
13
  Object.defineProperty(exports, "createClient", { enumerable: true, get: function () { return client_1.createClient; } });
14
14
  Object.defineProperty(exports, "resolveFlowAuth", { enumerable: true, get: function () { return client_1.resolveFlowAuth; } });
@@ -91,6 +91,9 @@ Object.defineProperty(exports, "DEFAULT_SUPER_CLASS", { enumerable: true, get: f
91
91
  Object.defineProperty(exports, "DEFAULT_SAVE_ACTION", { enumerable: true, get: function () { return table_1.DEFAULT_SAVE_ACTION; } });
92
92
  Object.defineProperty(exports, "addColumn", { enumerable: true, get: function () { return table_1.addColumn; } });
93
93
  Object.defineProperty(exports, "deriveElement", { enumerable: true, get: function () { return table_1.deriveElement; } });
94
+ Object.defineProperty(exports, "ensureDesignAccess", { enumerable: true, get: function () { return table_1.ensureDesignAccess; } });
95
+ Object.defineProperty(exports, "findDesignAccess", { enumerable: true, get: function () { return table_1.findDesignAccess; } });
96
+ Object.defineProperty(exports, "DESIGN_ACCESS_TABLE", { enumerable: true, get: function () { return table_1.DESIGN_ACCESS_TABLE; } });
94
97
  Object.defineProperty(exports, "addIndex", { enumerable: true, get: function () { return table_1.addIndex; } });
95
98
  Object.defineProperty(exports, "parseIndexColumns", { enumerable: true, get: function () { return table_1.parseIndexColumns; } });
96
99
  Object.defineProperty(exports, "indexMatchesColumns", { enumerable: true, get: function () { return table_1.indexMatchesColumns; } });
@@ -10,7 +10,7 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
10
  import { z } from "zod";
11
11
  import type { ToolAnnotations } from "@tenonhq/dovetail-mcp-kit";
12
12
  import type { ServiceNowClient } from "../client";
13
- export declare var TOOL_NAMES: readonly ["create_view", "set_list_layout", "set_form_layout", "set_related_lists", "add_choices_to_field", "remove_choices_from_field", "flow_view", "action_view", "action_edit", "action_clone", "action_define", "flow_publish", "flow_copy", "flow_create", "flow_test", "flow_edit", "create_table", "add_column", "add_index", "index_list", "index_create", "set_column", "set_table", "set_field", "create_record", "delete_record", "host_assets", "invoke_rest", "app_publish", "update_set_export", "app_export"];
13
+ export declare var TOOL_NAMES: readonly ["create_view", "set_list_layout", "set_form_layout", "set_related_lists", "add_choices_to_field", "remove_choices_from_field", "flow_view", "action_view", "action_edit", "action_clone", "action_define", "flow_publish", "flow_copy", "flow_create", "flow_test", "flow_edit", "create_table", "add_column", "design_access", "add_index", "index_list", "index_create", "set_column", "set_table", "set_field", "create_record", "delete_record", "host_assets", "invoke_rest", "app_publish", "update_set_export", "app_export"];
14
14
  export type ToolName = (typeof TOOL_NAMES)[number];
15
15
  export interface RegistryDeps {
16
16
  /** Optional client injection for tests; defaults to createClient({}). */
@@ -57,6 +57,7 @@ exports.TOOL_NAMES = [
57
57
  "flow_edit",
58
58
  "create_table",
59
59
  "add_column",
60
+ "design_access",
60
61
  "add_index",
61
62
  "index_list",
62
63
  "index_create",
@@ -440,7 +441,17 @@ function buildDescriptors(deps = {}) {
440
441
  "table NAME; element is derived from label unless column.name is given. dependent_on_field names " +
441
442
  "the sibling column a document_id column resolves against (its table_name column) — it must " +
442
443
  "already exist on the table, is verified on the read-back, and is refused when absent. " +
443
- "updateSetSysId is required on the live path. dryRun:true returns the plan with no writes.",
444
+ "scope must match the table's scope unless crossScope:true, which opts in to a column OWNED " +
445
+ "by scope on another app's table (the platform's cross-scope field: element becomes " +
446
+ "<scope>_<name>, the dictionary row and its update-set capture land in scope); the table " +
447
+ "must allow new fields from other scopes (sys_db_object.alter_access) and updateSetSysId " +
448
+ "must belong to scope — both checked on dryRun too, and the stored element + sys_scope are " +
449
+ "read back. A cross-scope result carries designAccess { present, sysId, created }: the " +
450
+ "platform UI refuses cross-scope authoring without a sys_scope_design_access record " +
451
+ "(scope -> the table's scope), so a missing one is FLAGGED (not blocking — the headless " +
452
+ "insert works without it); ensureDesignAccess:true creates it first in the same update " +
453
+ "set. updateSetSysId is required on the live path. dryRun:true returns the plan with " +
454
+ "no writes.",
444
455
  shape: schemas_1.addColumnSchema.shape,
445
456
  handler: async function (args) {
446
457
  var p = schemas_1.addColumnSchema.parse(args);
@@ -458,12 +469,44 @@ function buildDescriptors(deps = {}) {
458
469
  table: p.table,
459
470
  column: p.column,
460
471
  scope: p.scope,
472
+ crossScope: p.crossScope,
473
+ ensureDesignAccess: p.ensureDesignAccess,
461
474
  updateSetSysId: p.updateSetSysId,
462
475
  dryRun: p.dryRun,
463
476
  debug: p.debug,
464
477
  });
465
478
  },
466
479
  },
480
+ {
481
+ name: "design_access",
482
+ annotations: dovetail_mcp_kit_1.WRITE_CREATE,
483
+ description: "Ensure the sys_scope_design_access record that lets sourceScope (the AUTHORING app) " +
484
+ "design in targetScope's tables (the app that OWNS them). The platform UI requires it " +
485
+ "for cross-scope columns — the symptom is \"Invalid 'Table' selected on the Dictionary " +
486
+ "Entry record ... can only select '<app>' tables with read access enabled\", and the " +
487
+ "table's own access flags are NOT the gate. Scopes are names or sys_scope sys_ids. " +
488
+ "Idempotent: an existing record returns status 'exists' and nothing is written. Live " +
489
+ "creates it via the scope-aware createRecord op switched to sourceScope, captured in " +
490
+ "updateSetSysId (must belong to sourceScope), then reads it back and asserts " +
491
+ "source/target. updateSetSysId is required on the live path; dryRun:true only reports " +
492
+ "exists / missing.",
493
+ shape: schemas_1.designAccessSchema.shape,
494
+ handler: async function (args) {
495
+ var p = schemas_1.designAccessSchema.parse(args);
496
+ if (p.dryRun !== true &&
497
+ (!p.updateSetSysId || !p.updateSetSysId.trim())) {
498
+ throw new Error("design_access: updateSetSysId (an update set in sourceScope) is required on " +
499
+ "the live path — set dryRun:true to only check whether the record exists.");
500
+ }
501
+ return (0, table_1.ensureDesignAccess)({
502
+ client: client(),
503
+ sourceScope: p.sourceScope,
504
+ targetScope: p.targetScope,
505
+ updateSetSysId: p.updateSetSysId,
506
+ dryRun: p.dryRun,
507
+ });
508
+ },
509
+ },
467
510
  {
468
511
  name: "add_index",
469
512
  annotations: dovetail_mcp_kit_1.WRITE_OVERWRITE,
@@ -1890,6 +1890,8 @@ export declare var addColumnSchema: z.ZodObject<{
1890
1890
  dependent_on_field?: string | undefined;
1891
1891
  }>;
1892
1892
  scope: z.ZodOptional<z.ZodString>;
1893
+ crossScope: z.ZodOptional<z.ZodBoolean>;
1894
+ ensureDesignAccess: z.ZodOptional<z.ZodBoolean>;
1893
1895
  updateSetSysId: z.ZodOptional<z.ZodString>;
1894
1896
  dryRun: z.ZodOptional<z.ZodBoolean>;
1895
1897
  debug: z.ZodOptional<z.ZodBoolean>;
@@ -1909,6 +1911,8 @@ export declare var addColumnSchema: z.ZodObject<{
1909
1911
  debug?: boolean | undefined;
1910
1912
  updateSetSysId?: string | undefined;
1911
1913
  scope?: string | undefined;
1914
+ crossScope?: boolean | undefined;
1915
+ ensureDesignAccess?: boolean | undefined;
1912
1916
  }, {
1913
1917
  table: string;
1914
1918
  column: {
@@ -1925,6 +1929,24 @@ export declare var addColumnSchema: z.ZodObject<{
1925
1929
  debug?: boolean | undefined;
1926
1930
  updateSetSysId?: string | undefined;
1927
1931
  scope?: string | undefined;
1932
+ crossScope?: boolean | undefined;
1933
+ ensureDesignAccess?: boolean | undefined;
1934
+ }>;
1935
+ export declare var designAccessSchema: z.ZodObject<{
1936
+ sourceScope: z.ZodString;
1937
+ targetScope: z.ZodString;
1938
+ updateSetSysId: z.ZodOptional<z.ZodString>;
1939
+ dryRun: z.ZodOptional<z.ZodBoolean>;
1940
+ }, "strip", z.ZodTypeAny, {
1941
+ sourceScope: string;
1942
+ targetScope: string;
1943
+ dryRun?: boolean | undefined;
1944
+ updateSetSysId?: string | undefined;
1945
+ }, {
1946
+ sourceScope: string;
1947
+ targetScope: string;
1948
+ dryRun?: boolean | undefined;
1949
+ updateSetSysId?: string | undefined;
1928
1950
  }>;
1929
1951
  export declare var addIndexSchema: z.ZodObject<{
1930
1952
  table: z.ZodString;
@@ -4,7 +4,7 @@
4
4
  * own file so registry.ts stays focused on wiring.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.invokeRestSchema = exports.publishAppSchema = exports.exportAppSchema = exports.exportUpdateSetSchema = exports.deleteRecordSchema = exports.createRecordSchema = exports.setFieldSchema = exports.setTableSchema = exports.tableAttributesSchema = exports.setColumnSchema = exports.columnAttributesSchema = exports.createIndexSchema = exports.listIndexesSchema = exports.addIndexSchema = exports.addColumnSchema = exports.createTableSchema = exports.columnSpecSchema = exports.hostAssetsSchema = exports.editFlowSchema = exports.defineActionSchema = exports.defineActionSpecSchema = exports.cloneActionSchema = exports.editActionSchema = exports.stepInputPatchSchema = exports.testFlowSchema = exports.createFlowSchema = exports.copyFlowSchema = exports.publishFlowSchema = exports.viewActionSchema = exports.viewFlowSchema = exports.removeChoicesFromFieldSchema = exports.addChoicesToFieldSchema = exports.choiceValueSchema = exports.setRelatedListsSchema = exports.setFormLayoutSchema = exports.formSectionSchema = exports.setListLayoutSchema = exports.createViewSchema = void 0;
7
+ exports.invokeRestSchema = exports.publishAppSchema = exports.exportAppSchema = exports.exportUpdateSetSchema = exports.deleteRecordSchema = exports.createRecordSchema = exports.setFieldSchema = exports.setTableSchema = exports.tableAttributesSchema = exports.setColumnSchema = exports.columnAttributesSchema = exports.createIndexSchema = exports.listIndexesSchema = exports.addIndexSchema = exports.designAccessSchema = exports.addColumnSchema = exports.createTableSchema = exports.columnSpecSchema = exports.hostAssetsSchema = exports.editFlowSchema = exports.defineActionSchema = exports.defineActionSpecSchema = exports.cloneActionSchema = exports.editActionSchema = exports.stepInputPatchSchema = exports.testFlowSchema = exports.createFlowSchema = exports.copyFlowSchema = exports.publishFlowSchema = exports.viewActionSchema = exports.viewFlowSchema = exports.removeChoicesFromFieldSchema = exports.addChoicesToFieldSchema = exports.choiceValueSchema = exports.setRelatedListsSchema = exports.setFormLayoutSchema = exports.formSectionSchema = exports.setListLayoutSchema = exports.createViewSchema = void 0;
8
8
  const zod_1 = require("zod");
9
9
  exports.createViewSchema = zod_1.z.object({
10
10
  name: zod_1.z.string().min(1),
@@ -364,10 +364,27 @@ exports.addColumnSchema = zod_1.z.object({
364
364
  table: zod_1.z.string().min(1),
365
365
  column: exports.columnSpecSchema,
366
366
  scope: zod_1.z.string().optional(),
367
+ // Explicit opt-in to a column OWNED by `scope` when that differs from the table's
368
+ // scope (a Journey field on an Automate table). Without it a mismatched scope is
369
+ // refused — on dryRun and live alike — because that is the classic wrong-scope slip.
370
+ crossScope: zod_1.z.boolean().optional(),
371
+ // Cross-scope only: create the missing sys_scope_design_access record (scope -> the
372
+ // table's scope) first, in the same update set. Without it a missing record is flagged.
373
+ ensureDesignAccess: zod_1.z.boolean().optional(),
367
374
  updateSetSysId: zod_1.z.string().optional(),
368
375
  dryRun: zod_1.z.boolean().optional(),
369
376
  debug: zod_1.z.boolean().optional(),
370
377
  });
378
+ // design_access: ensure the sys_scope_design_access record that lets sourceScope author
379
+ // in targetScope's tables. updateSetSysId (an update set in sourceScope) is optional in
380
+ // the schema because dryRun needs none; the live-path requirement is enforced at the tool
381
+ // boundary (registry.ts), matching add_column.
382
+ exports.designAccessSchema = zod_1.z.object({
383
+ sourceScope: zod_1.z.string().min(1),
384
+ targetScope: zod_1.z.string().min(1),
385
+ updateSetSysId: zod_1.z.string().optional(),
386
+ dryRun: zod_1.z.boolean().optional(),
387
+ });
371
388
  // add-index keeps a column LIST because an index is conceptually multi-column, but the
372
389
  // only headless lever (sys_dictionary.unique) is per-COLUMN — so addIndex REFUSES a list
373
390
  // longer than one rather than silently building a different index than the one asked for.
@@ -16,8 +16,31 @@
16
16
  *
17
17
  * The scope arg passed to `createRecord` is the scope NAME (e.g. "x_cadso_core"),
18
18
  * not the sys_scope sys_id — the server-side changeScope matches on the scope name.
19
- * A column always belongs to its table's scope, so the name is resolved from the
20
- * table; an explicit `scope` override must match it.
19
+ * By default a column belongs to its table's scope, so the name is resolved from the
20
+ * table and an explicit `scope` override must match it.
21
+ *
22
+ * CROSS-SCOPE COLUMNS (`crossScope: true`). ServiceNow lets one app add a column to
23
+ * another app's table — Studio with app = Journey adding a field to an Automate table
24
+ * — and prefixes the element with the OWNING scope: `x_cadso_journey_instance_step`
25
+ * on `x_cadso_automate_email_batch`, dictionary row owned by x_cadso_journey. This is
26
+ * the column-ownership rule for Tenon's layered apps, so it is supported here as an
27
+ * explicit opt-in: `scope` names the column's scope, `crossScope: true` acknowledges it
28
+ * differs from the table's. The insert runs through the same scope-aware op switched
29
+ * to the COLUMN's scope, so the dictionary row and its update-set capture both land
30
+ * in the column's scope. Guards, run on dry-run and live alike: the override scope
31
+ * must exist; the table must allow new fields from other scopes
32
+ * (`sys_db_object.alter_access`); the update set must belong to the column's scope.
33
+ * The element is sent already prefixed (`<scope>_<name>`; an already-prefixed name
34
+ * is accepted as-is) and the element ServiceNow actually stored is read back and
35
+ * reported, together with a `sys_scope` assertion on the read-back row.
36
+ *
37
+ * DESIGN ACCESS. Since the Zurich bw40 hotfix the platform UI also requires a
38
+ * `sys_scope_design_access` record (source = the column's scope, target = the table's)
39
+ * before one app may author in another's tables. The headless insert here does not hit
40
+ * that check, so a missing record does not block the column — but it is FLAGGED on every
41
+ * cross-scope result (`designAccess`), because the next UI edit of the column, and the
42
+ * target instance, will. `ensureDesignAccess: true` creates it first, in the same update
43
+ * set (see ./designAccess.ts).
21
44
  *
22
45
  * SIZING THE PHYSICAL COLUMN. A max_length carried on the INSERT sets the dictionary
23
46
  * row but NOT the column ServiceNow actually builds — it materialises at the platform
@@ -46,8 +69,25 @@ export interface AddColumnParams {
46
69
  table: string;
47
70
  /** The single column to add. `name` (the element) is optional; derived from label when omitted. */
48
71
  column: ColumnSpec;
49
- /** Scope name or sys_scope sys_id. Must match the table's own scope (a column lives there). */
72
+ /**
73
+ * Scope name or sys_scope sys_id. Must match the table's own scope (a column lives
74
+ * there) — unless `crossScope` is true, in which case it names the scope that will OWN
75
+ * the column (e.g. "x_cadso_journey" for a Journey column on an Automate table).
76
+ */
50
77
  scope?: string;
78
+ /**
79
+ * Explicit opt-in to a column owned by a different scope than its table. Requires
80
+ * `scope`. The element is prefixed with the owning scope (`x_cadso_journey_<name>`),
81
+ * the table must allow new fields from other scopes (sys_db_object.alter_access),
82
+ * and the update set must belong to the column's scope.
83
+ */
84
+ crossScope?: boolean;
85
+ /**
86
+ * Cross-scope only: create the sys_scope_design_access record (column scope -> table
87
+ * scope) when it is missing, captured in the same update set, BEFORE the column insert.
88
+ * Without it a missing record is only flagged on the result.
89
+ */
90
+ ensureDesignAccess?: boolean;
51
91
  /** Update set sys_id to capture the insert into. REQUIRED on the live path (dry-run doesn't need it). */
52
92
  updateSetSysId?: string;
53
93
  /** Emit diagnostic detail in the result note. */
@@ -55,6 +95,21 @@ export interface AddColumnParams {
55
95
  /** Plan only — no writes. Pure + deterministic. */
56
96
  dryRun?: boolean;
57
97
  }
98
+ /** Design Access state for a cross-scope column (absent on a same-scope add). */
99
+ export interface DesignAccessFlag {
100
+ /** Always true when reported: a cross-scope column needs the record for UI/Studio work. */
101
+ required: true;
102
+ /** true = record exists (or was created); false = missing; null = could not be read. */
103
+ present: boolean | null;
104
+ /** sys_scope_design_access sys_id ("" when missing / unknown). */
105
+ sysId: string;
106
+ /** Authoring scope (the column's owner). */
107
+ sourceScope: string;
108
+ /** Table-owning scope. */
109
+ targetScope: string;
110
+ /** True when THIS run created the record. */
111
+ created: boolean;
112
+ }
58
113
  export interface AddColumnResult {
59
114
  status: "created" | "dry-run" | "failed" | "skipped";
60
115
  /** The table's name (resolved; echoes the input on dry-run). */
@@ -67,6 +122,11 @@ export interface AddColumnResult {
67
122
  label: string;
68
123
  /** Resolved ServiceNow internal_type (friendly -> internal). */
69
124
  internalType: string;
125
+ /**
126
+ * Scope NAME that owns the column ("" on a network-free dry-run). Equals the table's
127
+ * scope unless `crossScope` was used, in which case it is the override scope.
128
+ */
129
+ scope: string;
70
130
  /** sys_id of the sys_dictionary row (the insert's, or the existing row's on "skipped"). */
71
131
  columnSysId: string;
72
132
  /** Update set the write was captured into ("" on dry-run). */
@@ -75,6 +135,8 @@ export interface AddColumnResult {
75
135
  verified: boolean;
76
136
  /** Human-readable note (success summary, the read-back result, or the failure body). */
77
137
  note: string;
138
+ /** Cross-scope only: whether the Design Access record the platform UI requires exists. */
139
+ designAccess?: DesignAccessFlag;
78
140
  }
79
141
  /**
80
142
  * Derive the dictionary element (column name) from a label the way Studio does for
@@ -83,4 +145,9 @@ export interface AddColumnResult {
83
145
  * Pass `column.name` explicitly for anything non-trivial.
84
146
  */
85
147
  export declare function deriveElement(label: string, explicit?: string): string;
148
+ /**
149
+ * The Design Access sentence appended to a cross-scope result's note ("" when none).
150
+ * Exported for tests.
151
+ */
152
+ export declare function designAccessNote(da: DesignAccessFlag | undefined, ensureRequested: boolean, isDryRun: boolean): string;
86
153
  export declare function addColumn(params: AddColumnParams): Promise<AddColumnResult>;