@naturali/cli 0.136.0 → 0.137.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/index.mjs +1063 -45
  2. package/package.json +2 -2
package/dist/index.mjs CHANGED
@@ -17,7 +17,7 @@ var __exportAll = (all, no_symbols) => {
17
17
  };
18
18
  //#endregion
19
19
  //#region package.json
20
- var version = "0.136.0";
20
+ var version = "0.137.0";
21
21
  //#endregion
22
22
  //#region ../sdk/src/generated/core/bodySerializer.gen.ts
23
23
  const serializeFormDataPair = (data, key, value) => {
@@ -645,6 +645,17 @@ var Activity = class {
645
645
  ...options
646
646
  });
647
647
  }
648
+ /**
649
+ * Export the activity feed as NDJSON
650
+ *
651
+ * Streams a project's activity entries as newline-delimited JSON — one entry object per line, **oldest first**, where the feed itself reads newest first: a feed answers what just happened, and a file is read start to end. `project_id` is required: the export is per-project by design. Filters behave exactly as they do on the list endpoint.
652
+ */
653
+ static exportActivity(options) {
654
+ return (options.client ?? client).get({
655
+ url: "/v1/projects/{project_id}/activity/export",
656
+ ...options
657
+ });
658
+ }
648
659
  };
649
660
  var Actors = class {
650
661
  /**
@@ -2024,6 +2035,17 @@ var Documents = class {
2024
2035
  });
2025
2036
  }
2026
2037
  /**
2038
+ * Export documents as NDJSON
2039
+ *
2040
+ * Streams a project's documents as newline-delimited JSON — one document object per line, oldest first — for archiving the corpus or shipping it into another system. `project_id` is required: the export is per-project by design. The rows are the rows the listing returns for the same caller, so a policy that hides a document hides it here too, and withdrawn documents and the reserved `/.system/` root are left out.
2041
+ */
2042
+ static exportDocuments(options) {
2043
+ return (options.client ?? client).get({
2044
+ url: "/v1/projects/{project_id}/documents/export",
2045
+ ...options
2046
+ });
2047
+ }
2048
+ /**
2027
2049
  * Delete a document
2028
2050
  *
2029
2051
  * Deletes a document and its underlying file
@@ -2061,6 +2083,45 @@ var Documents = class {
2061
2083
  });
2062
2084
  }
2063
2085
  /**
2086
+ * List a document's relations
2087
+ *
2088
+ * Returns the typed edges this document asserts about others, oldest first. An edge is owned by the document it leaves, so this is what the document claims — use `?related_to=` on the listing to find what claims something about it.
2089
+ */
2090
+ static listDocumentRelations(options) {
2091
+ return (options.client ?? client).get({
2092
+ url: "/v1/projects/{project_id}/documents/{document_id}/relations",
2093
+ ...options
2094
+ });
2095
+ }
2096
+ /**
2097
+ * Assert a relation
2098
+ *
2099
+ * Asserts a typed edge from this document to another in the same project. Asserting the same edge twice is `409`: an edge is a fact, and the second assertion is the same fact.
2100
+ *
2101
+ * Writing an edge is a write of the asserting document (`documents:UpdateDocument`); the document it points at is unchanged, which is what lets an agent record what its own report derives from without being able to make another report claim anything.
2102
+ */
2103
+ static createDocumentRelation(options) {
2104
+ return (options.client ?? client).post({
2105
+ url: "/v1/projects/{project_id}/documents/{document_id}/relations",
2106
+ ...options,
2107
+ headers: {
2108
+ "Content-Type": "application/json",
2109
+ ...options.headers
2110
+ }
2111
+ });
2112
+ }
2113
+ /**
2114
+ * Retract a relation
2115
+ *
2116
+ * Removes an edge this document asserts. Both documents are left as they are — retracting a claim is not a change to what it was about.
2117
+ */
2118
+ static deleteDocumentRelation(options) {
2119
+ return (options.client ?? client).delete({
2120
+ url: "/v1/projects/{project_id}/documents/{document_id}/relations/{relation_id}",
2121
+ ...options
2122
+ });
2123
+ }
2124
+ /**
2064
2125
  * Get document ingestion status
2065
2126
  *
2066
2127
  * Returns a lightweight ingestion status payload for polling — `status`,
@@ -2078,6 +2139,70 @@ var Documents = class {
2078
2139
  });
2079
2140
  }
2080
2141
  /**
2142
+ * Withdraw a document
2143
+ *
2144
+ * Takes a document out of every default read while keeping its history. The withdrawal is archived as a **tombstone version**, so one mechanism answers what a document holds now and there is no second lifecycle flag for a reader to miss.
2145
+ *
2146
+ * Its chunks are dropped from the knowledge index, so a withdrawn document costs a live search nothing. The content is not lost with them: it is in the version before the tombstone, which [`POST /v1/projects/{project_id}/documents/{document_id}/versions/{version}/restore`](/docs/api/documents/restore-document-version) re-chunks from.
2147
+ *
2148
+ * `DELETE` stays what it is — permanent, and it removes the backing file. Withdrawal is the reversible act. It does not apply under `/.system/`: a platform-written document keeps the owning module's lifecycle.
2149
+ *
2150
+ */
2151
+ static withdrawDocument(options) {
2152
+ return (options.client ?? client).post({
2153
+ url: "/v1/projects/{project_id}/documents/{document_id}/withdraw",
2154
+ ...options,
2155
+ headers: {
2156
+ "Content-Type": "application/json",
2157
+ ...options.headers
2158
+ }
2159
+ });
2160
+ }
2161
+ /**
2162
+ * List a document's content versions
2163
+ *
2164
+ * Returns the document's archived states, newest first. A version is written on create and on every subsequent write that changes the content or its annotations, and a withdrawal is archived as a version carrying no content.
2165
+ *
2166
+ */
2167
+ static listDocumentVersions(options) {
2168
+ return (options.client ?? client).get({
2169
+ url: "/v1/projects/{project_id}/documents/{document_id}/versions",
2170
+ ...options
2171
+ });
2172
+ }
2173
+ /**
2174
+ * Fetch an archived document version
2175
+ *
2176
+ * Returns the exact content and annotations the document held at a given version, which is what lets a run that cited a version read what it read.
2177
+ *
2178
+ */
2179
+ static getDocumentVersion(options) {
2180
+ return (options.client ?? client).get({
2181
+ url: "/v1/projects/{project_id}/documents/{document_id}/versions/{version}",
2182
+ ...options
2183
+ });
2184
+ }
2185
+ /**
2186
+ * Restore an archived document version
2187
+ *
2188
+ * Writes an archived version's content and annotations back as the document's live state, which archives them again as a **new** version rather than rewinding the counter — so a run citing any version in between still resolves.
2189
+ *
2190
+ * The restore runs through the ordinary update path, so the content is re-chunked and re-embedded; restoring the state the document already holds is a no-op and archives nothing. Restoring any content version of a withdrawn document brings it back into listings and search.
2191
+ *
2192
+ * A tombstone version has no content, so naming one is `400 VALIDATION_FAILED`: restore the version before it instead.
2193
+ *
2194
+ */
2195
+ static restoreDocumentVersion(options) {
2196
+ return (options.client ?? client).post({
2197
+ url: "/v1/projects/{project_id}/documents/{document_id}/versions/{version}/restore",
2198
+ ...options,
2199
+ headers: {
2200
+ "Content-Type": "application/json",
2201
+ ...options.headers
2202
+ }
2203
+ });
2204
+ }
2205
+ /**
2081
2206
  * Re-ingest an existing document
2082
2207
  *
2083
2208
  * Re-runs ingestion for an existing document against its already-stored
@@ -3122,6 +3247,26 @@ var Memories = class {
3122
3247
  });
3123
3248
  }
3124
3249
  /**
3250
+ * Retract a memory
3251
+ *
3252
+ * Retires a fact that stopped holding with nothing replacing it. The memory leaves the default listing, [knowledge search](/docs/api/knowledge/search-knowledge) and write deduplication, so restating the fact later lands as a new memory.
3253
+ *
3254
+ * It is an invalidation with no successor, which is what tells it apart from a supersede: `invalidated_at` is set and `superseded_by_memory_id` stays null. The retraction is appended to the assertion ledger with outcome `retracted`, so who withdrew the fact is part of the record.
3255
+ *
3256
+ * The memory stays readable by id, with its text and its assertions. `DELETE` remains the way to remove it outright.
3257
+ *
3258
+ */
3259
+ static retractMemory(options) {
3260
+ return (options.client ?? client).post({
3261
+ url: "/v1/projects/{project_id}/memories/{memory_id}/retract",
3262
+ ...options,
3263
+ headers: {
3264
+ "Content-Type": "application/json",
3265
+ ...options.headers
3266
+ }
3267
+ });
3268
+ }
3269
+ /**
3125
3270
  * List a memory's assertions
3126
3271
  *
3127
3272
  * Returns every write that resolved into this memory, oldest first: the one that created it, the duplicates it absorbed, and the assertion that superseded another memory in its favour. A superseded memory keeps its own assertions, so the chain can be walked in both directions.
@@ -3315,6 +3460,17 @@ var MemoryStores = class {
3315
3460
  });
3316
3461
  }
3317
3462
  /**
3463
+ * Export a memory store's memories as NDJSON
3464
+ *
3465
+ * Streams one store's memories as newline-delimited JSON — one memory object per line, oldest first. Invalidated memories (retracted or superseded) are left out unless `include_invalidated` asks for them, so the file holds what the store currently asserts. The rows are the rows the listing returns for the same caller.
3466
+ */
3467
+ static exportMemories(options) {
3468
+ return (options.client ?? client).get({
3469
+ url: "/v1/projects/{project_id}/memory-stores/{memory_store_id}/export",
3470
+ ...options
3471
+ });
3472
+ }
3473
+ /**
3318
3474
  * Get memory store tags
3319
3475
  *
3320
3476
  * Returns all tags attached to the memory store
@@ -3356,6 +3512,92 @@ var MemoryStores = class {
3356
3512
  });
3357
3513
  }
3358
3514
  };
3515
+ var MetadataSchemas = class {
3516
+ /**
3517
+ * List metadata schemas
3518
+ *
3519
+ * Returns the declarations in scope, oldest first. `resource_type` narrows them to one governed resource.
3520
+ */
3521
+ static listMetadataSchemas(options) {
3522
+ return (options.client ?? client).get({
3523
+ url: "/v1/projects/{project_id}/metadata-schemas",
3524
+ ...options
3525
+ });
3526
+ }
3527
+ /**
3528
+ * Declare a metadata schema
3529
+ *
3530
+ * Declares what `metadata` must satisfy for one resource type under one selector. A document is selected by `path_prefix`, matched on a path boundary: `/reports` covers `/reports/q1.txt` and never `/reports-archive/q1.txt`.
3531
+ *
3532
+ * The schema is compiled here, so one JSON Schema cannot parse is refused rather than stored — a stored one would be a rule that silently governs nothing. One selector has one schema per resource type; a second declaration of the same one is `409 NAME_CONFLICT`. The reserved root `/.system` cannot be governed: a platform-written document carries no caller metadata.
3533
+ *
3534
+ */
3535
+ static createMetadataSchema(options) {
3536
+ return (options.client ?? client).post({
3537
+ url: "/v1/projects/{project_id}/metadata-schemas",
3538
+ ...options,
3539
+ headers: {
3540
+ "Content-Type": "application/json",
3541
+ ...options.headers
3542
+ }
3543
+ });
3544
+ }
3545
+ /**
3546
+ * Check metadata against what is declared
3547
+ *
3548
+ * Answers what a write would be told, without writing: a caller preparing a batch learns which declaration would refuse it, and why, before it sends anything.
3549
+ *
3550
+ * It reports; it does not enforce. The refusal itself lives in each resource's own write path, because a check a writer has to call is advisory and the writer who skips it is the one the rule exists for.
3551
+ *
3552
+ */
3553
+ static validateMetadata(options) {
3554
+ return (options.client ?? client).post({
3555
+ url: "/v1/projects/{project_id}/metadata-schemas/validate",
3556
+ ...options,
3557
+ headers: {
3558
+ "Content-Type": "application/json",
3559
+ ...options.headers
3560
+ }
3561
+ });
3562
+ }
3563
+ /**
3564
+ * Delete a metadata schema
3565
+ *
3566
+ * Removes the declaration. Documents already stored keep the metadata they hold — the rule governed writes, not rows.
3567
+ */
3568
+ static deleteMetadataSchema(options) {
3569
+ return (options.client ?? client).delete({
3570
+ url: "/v1/projects/{project_id}/metadata-schemas/{metadata_schema_id}",
3571
+ ...options
3572
+ });
3573
+ }
3574
+ /**
3575
+ * Get a metadata schema
3576
+ *
3577
+ * Returns one declaration by id.
3578
+ */
3579
+ static getMetadataSchema(options) {
3580
+ return (options.client ?? client).get({
3581
+ url: "/v1/projects/{project_id}/metadata-schemas/{metadata_schema_id}",
3582
+ ...options
3583
+ });
3584
+ }
3585
+ /**
3586
+ * Update a metadata schema
3587
+ *
3588
+ * Changes the schema, the selector, or both. `resource_type` is fixed at creation: it decides the selector's spelling and which write path reads the row, so changing it would silently repoint the declaration at a different door — delete it and declare again instead.
3589
+ */
3590
+ static updateMetadataSchema(options) {
3591
+ return (options.client ?? client).patch({
3592
+ url: "/v1/projects/{project_id}/metadata-schemas/{metadata_schema_id}",
3593
+ ...options,
3594
+ headers: {
3595
+ "Content-Type": "application/json",
3596
+ ...options.headers
3597
+ }
3598
+ });
3599
+ }
3600
+ };
3359
3601
  var ModelRoutes = class {
3360
3602
  /**
3361
3603
  * List model routes
@@ -4993,6 +5235,7 @@ var NaturaliClient = class {
4993
5235
  memories;
4994
5236
  memoryRules;
4995
5237
  memoryStores;
5238
+ metadataSchemas;
4996
5239
  modelRoutes;
4997
5240
  quotas;
4998
5241
  models;
@@ -5044,6 +5287,7 @@ var NaturaliClient = class {
5044
5287
  this.memories = bindResource(Memories, this.http);
5045
5288
  this.memoryRules = bindResource(MemoryRules, this.http);
5046
5289
  this.memoryStores = bindResource(MemoryStores, this.http);
5290
+ this.metadataSchemas = bindResource(MetadataSchemas, this.http);
5047
5291
  this.modelRoutes = bindResource(ModelRoutes, this.http);
5048
5292
  this.quotas = bindResource(Quotas, this.http);
5049
5293
  this.models = bindResource(Models, this.http);
@@ -5090,6 +5334,7 @@ var src_exports = /* @__PURE__ */ __exportAll({
5090
5334
  Memories: () => Memories,
5091
5335
  MemoryRules: () => MemoryRules,
5092
5336
  MemoryStores: () => MemoryStores,
5337
+ MetadataSchemas: () => MetadataSchemas,
5093
5338
  ModelRoutes: () => ModelRoutes,
5094
5339
  Models: () => Models,
5095
5340
  NaturaliClient: () => NaturaliClient,
@@ -5775,6 +6020,67 @@ const routes = {
5775
6020
  }
5776
6021
  ]
5777
6022
  },
6023
+ "export-activity": {
6024
+ serviceClass: "Activity",
6025
+ operationId: "exportActivity",
6026
+ description: "Streams a project's activity entries as newline-delimited JSON — one entry object per line, **oldest first**, where the feed itself reads newest first: a feed answers what just happened, and a file is read start to end. `project_id` is required: the export is per-project by design. Filters behave exactly as they do on the list endpoint.",
6027
+ moduleDocsUrl: "https://docs.naturali.ai/docs/modules/activity",
6028
+ httpMethod: "get",
6029
+ pathParams: ["project_id"],
6030
+ queryParams: [
6031
+ "kind",
6032
+ "severity",
6033
+ "agent_id",
6034
+ "generation_id",
6035
+ "orchestration_run_id"
6036
+ ],
6037
+ headerParams: [],
6038
+ cookieParams: [],
6039
+ flags: [
6040
+ {
6041
+ "name": "project_id",
6042
+ "description": "Project public ID (proj_ prefix).",
6043
+ "required": true,
6044
+ "type": "string",
6045
+ "in": "path"
6046
+ },
6047
+ {
6048
+ "name": "kind",
6049
+ "description": "Only entries of this kind",
6050
+ "required": false,
6051
+ "type": "string",
6052
+ "in": "query"
6053
+ },
6054
+ {
6055
+ "name": "severity",
6056
+ "description": "Only entries of this severity",
6057
+ "required": false,
6058
+ "type": "string",
6059
+ "in": "query"
6060
+ },
6061
+ {
6062
+ "name": "agent_id",
6063
+ "description": "Only entries an agent produced",
6064
+ "required": false,
6065
+ "type": "string",
6066
+ "in": "query"
6067
+ },
6068
+ {
6069
+ "name": "generation_id",
6070
+ "description": "Only entries a generation produced",
6071
+ "required": false,
6072
+ "type": "string",
6073
+ "in": "query"
6074
+ },
6075
+ {
6076
+ "name": "orchestration_run_id",
6077
+ "description": "Only entries an orchestration run produced",
6078
+ "required": false,
6079
+ "type": "string",
6080
+ "in": "query"
6081
+ }
6082
+ ]
6083
+ },
5778
6084
  "list-actors": {
5779
6085
  serviceClass: "Actors",
5780
6086
  operationId: "listActors",
@@ -6001,7 +6307,7 @@ const routes = {
6001
6307
  },
6002
6308
  {
6003
6309
  "name": "tags",
6004
- "description": "Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter and knowledge search.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.",
6310
+ "description": "Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter and knowledge search.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.",
6005
6311
  "required": false,
6006
6312
  "type": "object",
6007
6313
  "in": "body"
@@ -6937,6 +7243,13 @@ const routes = {
6937
7243
  "required": false,
6938
7244
  "type": "string",
6939
7245
  "in": "body"
7246
+ },
7247
+ {
7248
+ "name": "expected_version",
7249
+ "description": "Refuses the write unless the resource is at this version.",
7250
+ "required": false,
7251
+ "type": "string",
7252
+ "in": "body"
6940
7253
  }
6941
7254
  ]
6942
7255
  },
@@ -6948,7 +7261,7 @@ const routes = {
6948
7261
  httpMethod: "patch",
6949
7262
  pathParams: ["project_id", "agent_id"],
6950
7263
  queryParams: [],
6951
- headerParams: [],
7264
+ headerParams: ["If-Match"],
6952
7265
  cookieParams: [],
6953
7266
  flags: [
6954
7267
  {
@@ -6965,6 +7278,13 @@ const routes = {
6965
7278
  "type": "string",
6966
7279
  "in": "path"
6967
7280
  },
7281
+ {
7282
+ "name": "If-Match",
7283
+ "description": "The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.",
7284
+ "required": false,
7285
+ "type": "string",
7286
+ "in": "header"
7287
+ },
6968
7288
  {
6969
7289
  "name": "ai_provider_id",
6970
7290
  "description": "",
@@ -7118,6 +7438,13 @@ const routes = {
7118
7438
  "required": false,
7119
7439
  "type": "string",
7120
7440
  "in": "body"
7441
+ },
7442
+ {
7443
+ "name": "expected_version",
7444
+ "description": "Refuses the write unless the resource is at this version.",
7445
+ "required": false,
7446
+ "type": "string",
7447
+ "in": "body"
7121
7448
  }
7122
7449
  ]
7123
7450
  },
@@ -7254,7 +7581,7 @@ const routes = {
7254
7581
  "name": "metadata",
7255
7582
  "description": "Caller-supplied key/value metadata attached to the generation record for per-run audit attribution (e.g. the knowledge-corpus version that produced this action). Round-trips verbatim when the generation is fetched via the generations API. The bag is caller-owned and no key is reserved: server-owned state (usage attribution, the served agent version, the model route's record, the extraction summary) lives in its own top-level generation fields and cannot be written from here. Use the request's own `action_id` field to set the usage-attribution label.",
7256
7583
  "required": false,
7257
- "type": "object",
7584
+ "type": "string",
7258
7585
  "in": "body"
7259
7586
  },
7260
7587
  {
@@ -9695,9 +10022,9 @@ const routes = {
9695
10022
  },
9696
10023
  {
9697
10024
  "name": "metadata",
9698
- "description": "Optional structured metadata to attach to the message (e.g. phone number, channel). Stored as-is and injected into the AI prompt context.",
10025
+ "description": "Caller-owned annotations on the message (e.g. a phone number, a channel), stored as sent and returned verbatim. The platform does not read the bag, so nothing in it reaches the model: a value the model should see belongs in `message`.",
9699
10026
  "required": false,
9700
- "type": "object",
10027
+ "type": "string",
9701
10028
  "in": "body"
9702
10029
  }
9703
10030
  ]
@@ -9883,7 +10210,10 @@ const routes = {
9883
10210
  pathParams: ["project_id"],
9884
10211
  queryParams: [
9885
10212
  "path_prefix",
10213
+ "include_withdrawn",
10214
+ "related_to",
9886
10215
  "tags",
10216
+ "metadata",
9887
10217
  "limit",
9888
10218
  "offset"
9889
10219
  ],
@@ -9904,6 +10234,20 @@ const routes = {
9904
10234
  "type": "string",
9905
10235
  "in": "query"
9906
10236
  },
10237
+ {
10238
+ "name": "include_withdrawn",
10239
+ "description": "Include withdrawn documents. A withdrawn document leaves every default read and is not in the knowledge index at all — its chunks are dropped when it is withdrawn — so this shows it in the listing but never in a search.",
10240
+ "required": false,
10241
+ "type": "boolean",
10242
+ "in": "query"
10243
+ },
10244
+ {
10245
+ "name": "related_to",
10246
+ "description": "Only documents related to this one, on either side of the edge: what it points at, and what points at it. An id with no relations narrows the listing to nothing.",
10247
+ "required": false,
10248
+ "type": "string",
10249
+ "in": "query"
10250
+ },
9907
10251
  {
9908
10252
  "name": "tags",
9909
10253
  "description": "Filter by tag pairs, written `key:value` (split on the first colon, so a value may contain colons). Repeat the parameter for several pairs; **all** must be present with exactly that value.\n",
@@ -9911,6 +10255,13 @@ const routes = {
9911
10255
  "type": "array",
9912
10256
  "in": "query"
9913
10257
  },
10258
+ {
10259
+ "name": "metadata",
10260
+ "description": "A `MetadataFilter` as JSON, url-encoded. It travels as JSON rather than as `key:value` pairs because the match is exact and a query string cannot otherwise say whether `3` is the number or the string.",
10261
+ "required": false,
10262
+ "type": "string",
10263
+ "in": "query"
10264
+ },
9914
10265
  {
9915
10266
  "name": "limit",
9916
10267
  "description": "Maximum number of results to return",
@@ -9977,12 +10328,12 @@ const routes = {
9977
10328
  "name": "metadata",
9978
10329
  "description": "Arbitrary metadata object. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase.",
9979
10330
  "required": false,
9980
- "type": "object",
10331
+ "type": "string",
9981
10332
  "in": "body"
9982
10333
  },
9983
10334
  {
9984
10335
  "name": "tags",
9985
- "description": "Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter and knowledge search.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.",
10336
+ "description": "Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter and knowledge search.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.",
9986
10337
  "required": false,
9987
10338
  "type": "object",
9988
10339
  "in": "body"
@@ -10051,7 +10402,7 @@ const routes = {
10051
10402
  },
10052
10403
  {
10053
10404
  "name": "tags",
10054
- "description": "Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter and knowledge search.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.",
10405
+ "description": "Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter and knowledge search.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.",
10055
10406
  "required": false,
10056
10407
  "type": "object",
10057
10408
  "in": "body"
@@ -10079,10 +10430,34 @@ const routes = {
10079
10430
  }
10080
10431
  ]
10081
10432
  },
10082
- "get-document": {
10433
+ "export-documents": {
10083
10434
  serviceClass: "Documents",
10084
- operationId: "getDocument",
10085
- description: "Returns a document with its text content",
10435
+ operationId: "exportDocuments",
10436
+ description: "Streams a project's documents as newline-delimited JSON — one document object per line, oldest first — for archiving the corpus or shipping it into another system. `project_id` is required: the export is per-project by design. The rows are the rows the listing returns for the same caller, so a policy that hides a document hides it here too, and withdrawn documents and the reserved `/.system/` root are left out.",
10437
+ moduleDocsUrl: "https://docs.naturali.ai/docs/modules/documents",
10438
+ httpMethod: "get",
10439
+ pathParams: ["project_id"],
10440
+ queryParams: ["path_prefix"],
10441
+ headerParams: [],
10442
+ cookieParams: [],
10443
+ flags: [{
10444
+ "name": "project_id",
10445
+ "description": "Project public ID (proj_ prefix).",
10446
+ "required": true,
10447
+ "type": "string",
10448
+ "in": "path"
10449
+ }, {
10450
+ "name": "path_prefix",
10451
+ "description": "Only documents filed under this directory. The prefix is a path boundary, not a substring, exactly as on the listing.",
10452
+ "required": false,
10453
+ "type": "string",
10454
+ "in": "query"
10455
+ }]
10456
+ },
10457
+ "get-document": {
10458
+ serviceClass: "Documents",
10459
+ operationId: "getDocument",
10460
+ description: "Returns a document with its text content",
10086
10461
  moduleDocsUrl: "https://docs.naturali.ai/docs/modules/documents",
10087
10462
  httpMethod: "get",
10088
10463
  pathParams: ["project_id", "document_id"],
@@ -10111,7 +10486,7 @@ const routes = {
10111
10486
  httpMethod: "patch",
10112
10487
  pathParams: ["project_id", "document_id"],
10113
10488
  queryParams: [],
10114
- headerParams: [],
10489
+ headerParams: ["If-Match"],
10115
10490
  cookieParams: [],
10116
10491
  flags: [
10117
10492
  {
@@ -10128,6 +10503,13 @@ const routes = {
10128
10503
  "type": "string",
10129
10504
  "in": "path"
10130
10505
  },
10506
+ {
10507
+ "name": "If-Match",
10508
+ "description": "The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.",
10509
+ "required": false,
10510
+ "type": "string",
10511
+ "in": "header"
10512
+ },
10131
10513
  {
10132
10514
  "name": "content",
10133
10515
  "description": "New text content",
@@ -10151,17 +10533,24 @@ const routes = {
10151
10533
  },
10152
10534
  {
10153
10535
  "name": "metadata",
10154
- "description": "Arbitrary metadata object. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase.",
10536
+ "description": "Arbitrary metadata object, replacing the stored bag; `null` clears it. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase.",
10155
10537
  "required": false,
10156
- "type": "object",
10538
+ "type": "string",
10157
10539
  "in": "body"
10158
10540
  },
10159
10541
  {
10160
10542
  "name": "tags",
10161
- "description": "Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter and knowledge search.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.",
10543
+ "description": "Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter and knowledge search.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.",
10162
10544
  "required": false,
10163
10545
  "type": "object",
10164
10546
  "in": "body"
10547
+ },
10548
+ {
10549
+ "name": "expected_version",
10550
+ "description": "Refuses the write unless the document is at this version.",
10551
+ "required": false,
10552
+ "type": "string",
10553
+ "in": "body"
10165
10554
  }
10166
10555
  ]
10167
10556
  },
@@ -10189,6 +10578,109 @@ const routes = {
10189
10578
  "in": "path"
10190
10579
  }]
10191
10580
  },
10581
+ "list-document-relations": {
10582
+ serviceClass: "Documents",
10583
+ operationId: "listDocumentRelations",
10584
+ description: "Returns the typed edges this document asserts about others, oldest first. An edge is owned by the document it leaves, so this is what the document claims — use `?related_to=` on the listing to find what claims something about it.",
10585
+ moduleDocsUrl: "https://docs.naturali.ai/docs/modules/documents",
10586
+ httpMethod: "get",
10587
+ pathParams: ["project_id", "document_id"],
10588
+ queryParams: [],
10589
+ headerParams: [],
10590
+ cookieParams: [],
10591
+ flags: [{
10592
+ "name": "project_id",
10593
+ "description": "Project public ID (proj_ prefix).",
10594
+ "required": true,
10595
+ "type": "string",
10596
+ "in": "path"
10597
+ }, {
10598
+ "name": "document_id",
10599
+ "description": "",
10600
+ "required": true,
10601
+ "type": "string",
10602
+ "in": "path"
10603
+ }]
10604
+ },
10605
+ "create-document-relation": {
10606
+ serviceClass: "Documents",
10607
+ operationId: "createDocumentRelation",
10608
+ description: "Asserts a typed edge from this document to another in the same project. Asserting the same edge twice is `409`: an edge is a fact, and the second assertion is the same fact. Writing an edge is a write of the asserting document (`documents:UpdateDocument`); the document it points at is unchanged, which is what lets an agent record what its own report derives from without being able to make another report claim anything.",
10609
+ moduleDocsUrl: "https://docs.naturali.ai/docs/modules/documents",
10610
+ httpMethod: "post",
10611
+ pathParams: ["project_id", "document_id"],
10612
+ queryParams: [],
10613
+ headerParams: [],
10614
+ cookieParams: [],
10615
+ flags: [
10616
+ {
10617
+ "name": "project_id",
10618
+ "description": "Project public ID (proj_ prefix).",
10619
+ "required": true,
10620
+ "type": "string",
10621
+ "in": "path"
10622
+ },
10623
+ {
10624
+ "name": "document_id",
10625
+ "description": "",
10626
+ "required": true,
10627
+ "type": "string",
10628
+ "in": "path"
10629
+ },
10630
+ {
10631
+ "name": "type",
10632
+ "description": "What this document claims about the other",
10633
+ "required": true,
10634
+ "type": "string",
10635
+ "in": "body"
10636
+ },
10637
+ {
10638
+ "name": "to_document_id",
10639
+ "description": "The document the edge points at. Must be in the same project, and visible to the caller.",
10640
+ "required": true,
10641
+ "type": "string",
10642
+ "in": "body"
10643
+ }
10644
+ ]
10645
+ },
10646
+ "delete-document-relation": {
10647
+ serviceClass: "Documents",
10648
+ operationId: "deleteDocumentRelation",
10649
+ description: "Removes an edge this document asserts. Both documents are left as they are — retracting a claim is not a change to what it was about.",
10650
+ moduleDocsUrl: "https://docs.naturali.ai/docs/modules/documents",
10651
+ httpMethod: "delete",
10652
+ pathParams: [
10653
+ "project_id",
10654
+ "document_id",
10655
+ "relation_id"
10656
+ ],
10657
+ queryParams: [],
10658
+ headerParams: [],
10659
+ cookieParams: [],
10660
+ flags: [
10661
+ {
10662
+ "name": "project_id",
10663
+ "description": "Project public ID (proj_ prefix).",
10664
+ "required": true,
10665
+ "type": "string",
10666
+ "in": "path"
10667
+ },
10668
+ {
10669
+ "name": "document_id",
10670
+ "description": "",
10671
+ "required": true,
10672
+ "type": "string",
10673
+ "in": "path"
10674
+ },
10675
+ {
10676
+ "name": "relation_id",
10677
+ "description": "",
10678
+ "required": true,
10679
+ "type": "string",
10680
+ "in": "path"
10681
+ }
10682
+ ]
10683
+ },
10192
10684
  "get-document-status": {
10193
10685
  serviceClass: "Documents",
10194
10686
  operationId: "getDocumentStatus",
@@ -10213,6 +10705,178 @@ const routes = {
10213
10705
  "in": "path"
10214
10706
  }]
10215
10707
  },
10708
+ "withdraw-document": {
10709
+ serviceClass: "Documents",
10710
+ operationId: "withdrawDocument",
10711
+ description: "Takes a document out of every default read while keeping its history. The withdrawal is archived as a **tombstone version**, so one mechanism answers what a document holds now and there is no second lifecycle flag for a reader to miss. Its chunks are dropped from the knowledge index, so a withdrawn document costs a live search nothing. The content is not lost with them: it is in the version before the tombstone, which [`POST /v1/projects/{project_id}/documents/{document_id}/versions/{version}/restore`](/docs/api/documents/restore-document-version) re-chunks from. `DELETE` stays what it is — permanent, and it removes the backing file. Withdrawal is the reversible act. It does not apply under `/.system/`: a platform-written document keeps the owning module's lifecycle.",
10712
+ moduleDocsUrl: "https://docs.naturali.ai/docs/modules/documents",
10713
+ httpMethod: "post",
10714
+ pathParams: ["project_id", "document_id"],
10715
+ queryParams: [],
10716
+ headerParams: ["If-Match"],
10717
+ cookieParams: [],
10718
+ flags: [
10719
+ {
10720
+ "name": "project_id",
10721
+ "description": "Project public ID (proj_ prefix).",
10722
+ "required": true,
10723
+ "type": "string",
10724
+ "in": "path"
10725
+ },
10726
+ {
10727
+ "name": "document_id",
10728
+ "description": "",
10729
+ "required": true,
10730
+ "type": "string",
10731
+ "in": "path"
10732
+ },
10733
+ {
10734
+ "name": "If-Match",
10735
+ "description": "The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.",
10736
+ "required": false,
10737
+ "type": "string",
10738
+ "in": "header"
10739
+ },
10740
+ {
10741
+ "name": "version_label",
10742
+ "description": "Optional tag for the tombstone version this write archives.",
10743
+ "required": false,
10744
+ "type": "string",
10745
+ "in": "body"
10746
+ },
10747
+ {
10748
+ "name": "expected_version",
10749
+ "description": "Refuses the withdrawal unless the document is at this version.",
10750
+ "required": false,
10751
+ "type": "string",
10752
+ "in": "body"
10753
+ }
10754
+ ]
10755
+ },
10756
+ "list-document-versions": {
10757
+ serviceClass: "Documents",
10758
+ operationId: "listDocumentVersions",
10759
+ description: "Returns the document's archived states, newest first. A version is written on create and on every subsequent write that changes the content or its annotations, and a withdrawal is archived as a version carrying no content.",
10760
+ moduleDocsUrl: "https://docs.naturali.ai/docs/modules/documents",
10761
+ httpMethod: "get",
10762
+ pathParams: ["project_id", "document_id"],
10763
+ queryParams: ["limit", "offset"],
10764
+ headerParams: [],
10765
+ cookieParams: [],
10766
+ flags: [
10767
+ {
10768
+ "name": "project_id",
10769
+ "description": "Project public ID (proj_ prefix).",
10770
+ "required": true,
10771
+ "type": "string",
10772
+ "in": "path"
10773
+ },
10774
+ {
10775
+ "name": "document_id",
10776
+ "description": "",
10777
+ "required": true,
10778
+ "type": "string",
10779
+ "in": "path"
10780
+ },
10781
+ {
10782
+ "name": "limit",
10783
+ "description": "Maximum number of results to return",
10784
+ "required": false,
10785
+ "type": "integer",
10786
+ "in": "query"
10787
+ },
10788
+ {
10789
+ "name": "offset",
10790
+ "description": "Number of results to skip",
10791
+ "required": false,
10792
+ "type": "integer",
10793
+ "in": "query"
10794
+ }
10795
+ ]
10796
+ },
10797
+ "get-document-version": {
10798
+ serviceClass: "Documents",
10799
+ operationId: "getDocumentVersion",
10800
+ description: "Returns the exact content and annotations the document held at a given version, which is what lets a run that cited a version read what it read.",
10801
+ moduleDocsUrl: "https://docs.naturali.ai/docs/modules/documents",
10802
+ httpMethod: "get",
10803
+ pathParams: [
10804
+ "project_id",
10805
+ "document_id",
10806
+ "version"
10807
+ ],
10808
+ queryParams: [],
10809
+ headerParams: [],
10810
+ cookieParams: [],
10811
+ flags: [
10812
+ {
10813
+ "name": "project_id",
10814
+ "description": "Project public ID (proj_ prefix).",
10815
+ "required": true,
10816
+ "type": "string",
10817
+ "in": "path"
10818
+ },
10819
+ {
10820
+ "name": "document_id",
10821
+ "description": "",
10822
+ "required": true,
10823
+ "type": "string",
10824
+ "in": "path"
10825
+ },
10826
+ {
10827
+ "name": "version",
10828
+ "description": "",
10829
+ "required": true,
10830
+ "type": "integer",
10831
+ "in": "path"
10832
+ }
10833
+ ]
10834
+ },
10835
+ "restore-document-version": {
10836
+ serviceClass: "Documents",
10837
+ operationId: "restoreDocumentVersion",
10838
+ description: "Writes an archived version's content and annotations back as the document's live state, which archives them again as a **new** version rather than rewinding the counter — so a run citing any version in between still resolves. The restore runs through the ordinary update path, so the content is re-chunked and re-embedded; restoring the state the document already holds is a no-op and archives nothing. Restoring any content version of a withdrawn document brings it back into listings and search. A tombstone version has no content, so naming one is `400 VALIDATION_FAILED`: restore the version before it instead.",
10839
+ moduleDocsUrl: "https://docs.naturali.ai/docs/modules/documents",
10840
+ httpMethod: "post",
10841
+ pathParams: [
10842
+ "project_id",
10843
+ "document_id",
10844
+ "version"
10845
+ ],
10846
+ queryParams: [],
10847
+ headerParams: [],
10848
+ cookieParams: [],
10849
+ flags: [
10850
+ {
10851
+ "name": "project_id",
10852
+ "description": "Project public ID (proj_ prefix).",
10853
+ "required": true,
10854
+ "type": "string",
10855
+ "in": "path"
10856
+ },
10857
+ {
10858
+ "name": "document_id",
10859
+ "description": "",
10860
+ "required": true,
10861
+ "type": "string",
10862
+ "in": "path"
10863
+ },
10864
+ {
10865
+ "name": "version",
10866
+ "description": "",
10867
+ "required": true,
10868
+ "type": "integer",
10869
+ "in": "path"
10870
+ },
10871
+ {
10872
+ "name": "label",
10873
+ "description": "Optional tag for the version this restore archives. Defaults to `restored from v<version>`.",
10874
+ "required": false,
10875
+ "type": "string",
10876
+ "in": "body"
10877
+ }
10878
+ ]
10879
+ },
10216
10880
  "reingest-document": {
10217
10881
  serviceClass: "Documents",
10218
10882
  operationId: "reingestDocument",
@@ -10615,7 +11279,7 @@ const routes = {
10615
11279
  "name": "metadata",
10616
11280
  "description": "Free-form tags, opaque to the platform",
10617
11281
  "required": false,
10618
- "type": "object",
11282
+ "type": "string",
10619
11283
  "in": "body"
10620
11284
  }
10621
11285
  ]
@@ -10663,7 +11327,7 @@ const routes = {
10663
11327
  "name": "metadata",
10664
11328
  "description": "Free-form tags, opaque to the platform",
10665
11329
  "required": false,
10666
- "type": "object",
11330
+ "type": "string",
10667
11331
  "in": "body"
10668
11332
  }
10669
11333
  ]
@@ -10722,7 +11386,7 @@ const routes = {
10722
11386
  "name": "metadata",
10723
11387
  "description": "",
10724
11388
  "required": false,
10725
- "type": "object",
11389
+ "type": "string",
10726
11390
  "in": "body"
10727
11391
  }
10728
11392
  ]
@@ -11055,7 +11719,7 @@ const routes = {
11055
11719
  "name": "metadata",
11056
11720
  "description": "Caller-supplied key/value metadata attached to the run record for attribution — what this measurement was of (the commit or release candidate being scored, the CI job that asked for it). Round-trips verbatim on every read of the run, the list included.\n\nThe bag is caller-owned and no key is reserved: everything the platform decides about a run (`status`, `agent_version`, `baseline_run_id`, `aggregate_scores`, `passed`, the counts) is a field of its own and cannot be written from here. Nothing in the scoring path reads it. A non-object is rejected with `400 VALIDATION_FAILED` and no run is created.",
11057
11721
  "required": false,
11058
- "type": "object",
11722
+ "type": "string",
11059
11723
  "in": "body"
11060
11724
  },
11061
11725
  {
@@ -11439,9 +12103,9 @@ const routes = {
11439
12103
  },
11440
12104
  {
11441
12105
  "name": "metadata",
11442
- "description": "JSON string with additional metadata",
12106
+ "description": "Caller-owned annotations on a resource, stored as the object they were written as: the types a value was written with are the types a read returns, so a filter can ask an ordering question about a number. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase.\n\nNo key is reserved, and that is the point: every piece of state the platform owns lives in its own typed column, so nothing written here reaches platform state. The platform never reads the bag — it is not an IAM context, not a policy input and not part of a prompt — which is what separates it from a tag bag.",
11443
12107
  "required": false,
11444
- "type": "string",
12108
+ "type": "object",
11445
12109
  "in": "body"
11446
12110
  }
11447
12111
  ]
@@ -11512,9 +12176,9 @@ const routes = {
11512
12176
  },
11513
12177
  {
11514
12178
  "name": "metadata",
11515
- "description": "JSON string with additional metadata",
12179
+ "description": "Caller-owned annotations on a resource, stored as the object they were written as: the types a value was written with are the types a read returns, so a filter can ask an ordering question about a number. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase.\n\nNo key is reserved, and that is the point: every piece of state the platform owns lives in its own typed column, so nothing written here reaches platform state. The platform never reads the bag — it is not an IAM context, not a policy input and not part of a prompt — which is what separates it from a tag bag.",
11516
12180
  "required": false,
11517
- "type": "string",
12181
+ "type": "object",
11518
12182
  "in": "body"
11519
12183
  }
11520
12184
  ]
@@ -11628,7 +12292,7 @@ const routes = {
11628
12292
  },
11629
12293
  {
11630
12294
  "name": "metadata",
11631
- "description": "New metadata as a JSON string",
12295
+ "description": "Replaces the stored bag; `null` clears it.",
11632
12296
  "required": false,
11633
12297
  "type": "string",
11634
12298
  "in": "body"
@@ -11907,7 +12571,7 @@ const routes = {
11907
12571
  "name": "metadata",
11908
12572
  "description": "Static annotations stored on the formation record. This field is NOT a substitution site: `sub`/`param`/`ref` expressions are rejected with 400 (`FORMATION_INVALID_METADATA`). For deploy-time substitution use the template's top-level `metadata` block, which is resolved into `resolved_metadata`.\n",
11909
12573
  "required": false,
11910
- "type": "object",
12574
+ "type": "string",
11911
12575
  "in": "body"
11912
12576
  }
11913
12577
  ]
@@ -11979,7 +12643,7 @@ const routes = {
11979
12643
  "name": "metadata",
11980
12644
  "description": "Static annotations stored on the formation record. This field is NOT a substitution site: `sub`/`param`/`ref` expressions are rejected with 400 (`FORMATION_INVALID_METADATA`). For deploy-time substitution use the template's top-level `metadata` block, which is resolved into `resolved_metadata`.\n",
11981
12645
  "required": false,
11982
- "type": "object",
12646
+ "type": "string",
11983
12647
  "in": "body"
11984
12648
  }
11985
12649
  ]
@@ -12211,7 +12875,7 @@ const routes = {
12211
12875
  "name": "metadata",
12212
12876
  "description": "Caller-supplied key/value metadata to shallow-merge into the generation record's caller-owned `metadata` bag. No key is reserved: server-owned state lives in its own top-level fields and cannot be written from here.\n",
12213
12877
  "required": true,
12214
- "type": "object",
12878
+ "type": "string",
12215
12879
  "in": "body"
12216
12880
  }
12217
12881
  ]
@@ -12392,7 +13056,7 @@ const routes = {
12392
13056
  httpMethod: "patch",
12393
13057
  pathParams: ["project_id", "guardrail_id"],
12394
13058
  queryParams: [],
12395
- headerParams: [],
13059
+ headerParams: ["If-Match"],
12396
13060
  cookieParams: [],
12397
13061
  flags: [
12398
13062
  {
@@ -12409,6 +13073,13 @@ const routes = {
12409
13073
  "type": "string",
12410
13074
  "in": "path"
12411
13075
  },
13076
+ {
13077
+ "name": "If-Match",
13078
+ "description": "The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.",
13079
+ "required": false,
13080
+ "type": "string",
13081
+ "in": "header"
13082
+ },
12412
13083
  {
12413
13084
  "name": "name",
12414
13085
  "description": "",
@@ -12450,6 +13121,13 @@ const routes = {
12450
13121
  "required": false,
12451
13122
  "type": "string",
12452
13123
  "in": "body"
13124
+ },
13125
+ {
13126
+ "name": "expected_version",
13127
+ "description": "Refuses the write unless the resource is at this version.",
13128
+ "required": false,
13129
+ "type": "string",
13130
+ "in": "body"
12453
13131
  }
12454
13132
  ]
12455
13133
  },
@@ -12775,7 +13453,7 @@ const routes = {
12775
13453
  "name": "metadata",
12776
13454
  "description": "Arbitrary JSON metadata",
12777
13455
  "required": false,
12778
- "type": "object",
13456
+ "type": "string",
12779
13457
  "in": "body"
12780
13458
  }
12781
13459
  ]
@@ -12901,7 +13579,7 @@ const routes = {
12901
13579
  },
12902
13580
  {
12903
13581
  "name": "metadata",
12904
- "description": "",
13582
+ "description": "A `MetadataBag` on a field where `null` is meaningful — a full-replacement update that clears the bag, or a record whose bag was never set.",
12905
13583
  "required": false,
12906
13584
  "type": "object",
12907
13585
  "in": "body"
@@ -13026,6 +13704,13 @@ const routes = {
13026
13704
  "required": false,
13027
13705
  "type": "string",
13028
13706
  "in": "body"
13707
+ },
13708
+ {
13709
+ "name": "metadata",
13710
+ "description": "Filter document results by their `metadata` bag. A document-store filter: a memory carries no such bag, so passing it alone searches documents, as `document_paths` does.",
13711
+ "required": false,
13712
+ "type": "string",
13713
+ "in": "body"
13029
13714
  }
13030
13715
  ]
13031
13716
  },
@@ -13083,7 +13768,7 @@ const routes = {
13083
13768
  },
13084
13769
  {
13085
13770
  "name": "include_invalidated",
13086
- "description": "Include invalidated (superseded) memories. They are excluded by default; set this to audit the supersede history.",
13771
+ "description": "Include invalidated memories — superseded or retracted. They are excluded by default; set this to audit what a store once held.",
13087
13772
  "required": false,
13088
13773
  "type": "boolean",
13089
13774
  "in": "query"
@@ -13147,7 +13832,7 @@ const routes = {
13147
13832
  "name": "metadata",
13148
13833
  "description": "Arbitrary structured metadata attached to the memory",
13149
13834
  "required": false,
13150
- "type": "object",
13835
+ "type": "string",
13151
13836
  "in": "body"
13152
13837
  },
13153
13838
  {
@@ -13205,7 +13890,7 @@ const routes = {
13205
13890
  httpMethod: "put",
13206
13891
  pathParams: ["project_id", "memory_id"],
13207
13892
  queryParams: [],
13208
- headerParams: [],
13893
+ headerParams: ["If-Match"],
13209
13894
  cookieParams: [],
13210
13895
  flags: [
13211
13896
  {
@@ -13222,6 +13907,13 @@ const routes = {
13222
13907
  "type": "string",
13223
13908
  "in": "path"
13224
13909
  },
13910
+ {
13911
+ "name": "If-Match",
13912
+ "description": "The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.",
13913
+ "required": false,
13914
+ "type": "string",
13915
+ "in": "header"
13916
+ },
13225
13917
  {
13226
13918
  "name": "content",
13227
13919
  "description": "Updated text content",
@@ -13237,10 +13929,17 @@ const routes = {
13237
13929
  "in": "body"
13238
13930
  },
13239
13931
  {
13240
- "name": "metadata",
13241
- "description": "Replaces the memory's metadata. Pass null to clear.",
13932
+ "name": "metadata",
13933
+ "description": "Replaces the memory's metadata. Pass null to clear.",
13934
+ "required": false,
13935
+ "type": "string",
13936
+ "in": "body"
13937
+ },
13938
+ {
13939
+ "name": "expected_version",
13940
+ "description": "Refuses the write unless the memory is at this version.",
13242
13941
  "required": false,
13243
- "type": "object",
13942
+ "type": "string",
13244
13943
  "in": "body"
13245
13944
  }
13246
13945
  ]
@@ -13269,6 +13968,47 @@ const routes = {
13269
13968
  "in": "path"
13270
13969
  }]
13271
13970
  },
13971
+ "retract-memory": {
13972
+ serviceClass: "Memories",
13973
+ operationId: "retractMemory",
13974
+ description: "Retires a fact that stopped holding with nothing replacing it. The memory leaves the default listing, [knowledge search](/docs/api/knowledge/search-knowledge) and write deduplication, so restating the fact later lands as a new memory. It is an invalidation with no successor, which is what tells it apart from a supersede: `invalidated_at` is set and `superseded_by_memory_id` stays null. The retraction is appended to the assertion ledger with outcome `retracted`, so who withdrew the fact is part of the record. The memory stays readable by id, with its text and its assertions. `DELETE` remains the way to remove it outright.",
13975
+ moduleDocsUrl: "https://docs.naturali.ai/docs/modules/memories",
13976
+ httpMethod: "post",
13977
+ pathParams: ["project_id", "memory_id"],
13978
+ queryParams: [],
13979
+ headerParams: ["If-Match"],
13980
+ cookieParams: [],
13981
+ flags: [
13982
+ {
13983
+ "name": "project_id",
13984
+ "description": "Project public ID (proj_ prefix).",
13985
+ "required": true,
13986
+ "type": "string",
13987
+ "in": "path"
13988
+ },
13989
+ {
13990
+ "name": "memory_id",
13991
+ "description": "",
13992
+ "required": true,
13993
+ "type": "string",
13994
+ "in": "path"
13995
+ },
13996
+ {
13997
+ "name": "If-Match",
13998
+ "description": "The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.",
13999
+ "required": false,
14000
+ "type": "string",
14001
+ "in": "header"
14002
+ },
14003
+ {
14004
+ "name": "expected_version",
14005
+ "description": "Refuses the retraction unless the memory is at this version.",
14006
+ "required": false,
14007
+ "type": "string",
14008
+ "in": "body"
14009
+ }
14010
+ ]
14011
+ },
13272
14012
  "list-memory-assertions": {
13273
14013
  serviceClass: "Memories",
13274
14014
  operationId: "listMemoryAssertions",
@@ -13955,6 +14695,47 @@ const routes = {
13955
14695
  }
13956
14696
  ]
13957
14697
  },
14698
+ "export-memories": {
14699
+ serviceClass: "MemoryStores",
14700
+ operationId: "exportMemories",
14701
+ description: "Streams one store's memories as newline-delimited JSON — one memory object per line, oldest first. Invalidated memories (retracted or superseded) are left out unless `include_invalidated` asks for them, so the file holds what the store currently asserts. The rows are the rows the listing returns for the same caller.",
14702
+ moduleDocsUrl: "https://docs.naturali.ai/docs/modules/memory-stores",
14703
+ httpMethod: "get",
14704
+ pathParams: ["project_id", "memory_store_id"],
14705
+ queryParams: ["include_invalidated", "tags"],
14706
+ headerParams: [],
14707
+ cookieParams: [],
14708
+ flags: [
14709
+ {
14710
+ "name": "project_id",
14711
+ "description": "Project public ID (proj_ prefix).",
14712
+ "required": true,
14713
+ "type": "string",
14714
+ "in": "path"
14715
+ },
14716
+ {
14717
+ "name": "memory_store_id",
14718
+ "description": "Memory store whose memories are exported",
14719
+ "required": true,
14720
+ "type": "string",
14721
+ "in": "path"
14722
+ },
14723
+ {
14724
+ "name": "include_invalidated",
14725
+ "description": "Include memories that were retracted or superseded",
14726
+ "required": false,
14727
+ "type": "boolean",
14728
+ "in": "query"
14729
+ },
14730
+ {
14731
+ "name": "tags",
14732
+ "description": "Filter by tag pairs, written `key:value` (split on the first colon, so a value may contain colons). Repeat the parameter for several pairs; **all** must be present with exactly that value.\n",
14733
+ "required": false,
14734
+ "type": "array",
14735
+ "in": "query"
14736
+ }
14737
+ ]
14738
+ },
13958
14739
  "get-memory-store-tags": {
13959
14740
  serviceClass: "MemoryStores",
13960
14741
  operationId: "getMemoryStoreTags",
@@ -14027,6 +14808,215 @@ const routes = {
14027
14808
  "in": "path"
14028
14809
  }]
14029
14810
  },
14811
+ "list-metadata-schemas": {
14812
+ serviceClass: "MetadataSchemas",
14813
+ operationId: "listMetadataSchemas",
14814
+ description: "Returns the declarations in scope, oldest first. `resource_type` narrows them to one governed resource.",
14815
+ moduleDocsUrl: "https://docs.naturali.ai/docs/modules/metadata-schemas",
14816
+ httpMethod: "get",
14817
+ pathParams: ["project_id"],
14818
+ queryParams: [
14819
+ "resource_type",
14820
+ "limit",
14821
+ "offset"
14822
+ ],
14823
+ headerParams: [],
14824
+ cookieParams: [],
14825
+ flags: [
14826
+ {
14827
+ "name": "project_id",
14828
+ "description": "Project public ID (proj_ prefix).",
14829
+ "required": true,
14830
+ "type": "string",
14831
+ "in": "path"
14832
+ },
14833
+ {
14834
+ "name": "resource_type",
14835
+ "description": "Return only the declarations governing this resource.",
14836
+ "required": false,
14837
+ "type": "string",
14838
+ "in": "query"
14839
+ },
14840
+ {
14841
+ "name": "limit",
14842
+ "description": "Maximum number of results to return",
14843
+ "required": false,
14844
+ "type": "integer",
14845
+ "in": "query"
14846
+ },
14847
+ {
14848
+ "name": "offset",
14849
+ "description": "Number of results to skip",
14850
+ "required": false,
14851
+ "type": "integer",
14852
+ "in": "query"
14853
+ }
14854
+ ]
14855
+ },
14856
+ "create-metadata-schema": {
14857
+ serviceClass: "MetadataSchemas",
14858
+ operationId: "createMetadataSchema",
14859
+ description: "Declares what `metadata` must satisfy for one resource type under one selector. A document is selected by `path_prefix`, matched on a path boundary: `/reports` covers `/reports/q1.txt` and never `/reports-archive/q1.txt`. The schema is compiled here, so one JSON Schema cannot parse is refused rather than stored — a stored one would be a rule that silently governs nothing. One selector has one schema per resource type; a second declaration of the same one is `409 NAME_CONFLICT`. The reserved root `/.system` cannot be governed: a platform-written document carries no caller metadata.",
14860
+ moduleDocsUrl: "https://docs.naturali.ai/docs/modules/metadata-schemas",
14861
+ httpMethod: "post",
14862
+ pathParams: ["project_id"],
14863
+ queryParams: [],
14864
+ headerParams: [],
14865
+ cookieParams: [],
14866
+ flags: [
14867
+ {
14868
+ "name": "project_id",
14869
+ "description": "Project public ID (proj_ prefix).",
14870
+ "required": true,
14871
+ "type": "string",
14872
+ "in": "path"
14873
+ },
14874
+ {
14875
+ "name": "resource_type",
14876
+ "description": "The resource whose metadata this declaration governs. A type appears here once its write path reads the registry, so a declaration always has a door that enforces it.",
14877
+ "required": true,
14878
+ "type": "string",
14879
+ "in": "body"
14880
+ },
14881
+ {
14882
+ "name": "path_prefix",
14883
+ "description": "The selector a `document` declaration must carry: the directory it governs.",
14884
+ "required": false,
14885
+ "type": "string",
14886
+ "in": "body"
14887
+ },
14888
+ {
14889
+ "name": "schema",
14890
+ "description": "A JSON Schema. Its keywords are its own vocabulary and are stored as written.",
14891
+ "required": true,
14892
+ "type": "object",
14893
+ "in": "body"
14894
+ }
14895
+ ]
14896
+ },
14897
+ "validate-metadata": {
14898
+ serviceClass: "MetadataSchemas",
14899
+ operationId: "validateMetadata",
14900
+ description: "Answers what a write would be told, without writing: a caller preparing a batch learns which declaration would refuse it, and why, before it sends anything. It reports; it does not enforce. The refusal itself lives in each resource's own write path, because a check a writer has to call is advisory and the writer who skips it is the one the rule exists for.",
14901
+ moduleDocsUrl: "https://docs.naturali.ai/docs/modules/metadata-schemas",
14902
+ httpMethod: "post",
14903
+ pathParams: ["project_id"],
14904
+ queryParams: [],
14905
+ headerParams: [],
14906
+ cookieParams: [],
14907
+ flags: [
14908
+ {
14909
+ "name": "project_id",
14910
+ "description": "Project public ID (proj_ prefix).",
14911
+ "required": true,
14912
+ "type": "string",
14913
+ "in": "path"
14914
+ },
14915
+ {
14916
+ "name": "path",
14917
+ "description": "The path the document would be filed at, which decides which declaration governs it.",
14918
+ "required": true,
14919
+ "type": "string",
14920
+ "in": "body"
14921
+ },
14922
+ {
14923
+ "name": "metadata",
14924
+ "description": "The bag to judge. Absent or `null` is judged as an empty bag.",
14925
+ "required": false,
14926
+ "type": "string",
14927
+ "in": "body"
14928
+ }
14929
+ ]
14930
+ },
14931
+ "get-metadata-schema": {
14932
+ serviceClass: "MetadataSchemas",
14933
+ operationId: "getMetadataSchema",
14934
+ description: "Returns one declaration by id.",
14935
+ moduleDocsUrl: "https://docs.naturali.ai/docs/modules/metadata-schemas",
14936
+ httpMethod: "get",
14937
+ pathParams: ["project_id", "metadata_schema_id"],
14938
+ queryParams: [],
14939
+ headerParams: [],
14940
+ cookieParams: [],
14941
+ flags: [{
14942
+ "name": "project_id",
14943
+ "description": "Project public ID (proj_ prefix).",
14944
+ "required": true,
14945
+ "type": "string",
14946
+ "in": "path"
14947
+ }, {
14948
+ "name": "metadata_schema_id",
14949
+ "description": "",
14950
+ "required": true,
14951
+ "type": "string",
14952
+ "in": "path"
14953
+ }]
14954
+ },
14955
+ "update-metadata-schema": {
14956
+ serviceClass: "MetadataSchemas",
14957
+ operationId: "updateMetadataSchema",
14958
+ description: "Changes the schema, the selector, or both. `resource_type` is fixed at creation: it decides the selector's spelling and which write path reads the row, so changing it would silently repoint the declaration at a different door — delete it and declare again instead.",
14959
+ moduleDocsUrl: "https://docs.naturali.ai/docs/modules/metadata-schemas",
14960
+ httpMethod: "patch",
14961
+ pathParams: ["project_id", "metadata_schema_id"],
14962
+ queryParams: [],
14963
+ headerParams: [],
14964
+ cookieParams: [],
14965
+ flags: [
14966
+ {
14967
+ "name": "project_id",
14968
+ "description": "Project public ID (proj_ prefix).",
14969
+ "required": true,
14970
+ "type": "string",
14971
+ "in": "path"
14972
+ },
14973
+ {
14974
+ "name": "metadata_schema_id",
14975
+ "description": "",
14976
+ "required": true,
14977
+ "type": "string",
14978
+ "in": "path"
14979
+ },
14980
+ {
14981
+ "name": "path_prefix",
14982
+ "description": "The declaration's new selector.",
14983
+ "required": false,
14984
+ "type": "string",
14985
+ "in": "body"
14986
+ },
14987
+ {
14988
+ "name": "schema",
14989
+ "description": "Replaces the declared JSON Schema.",
14990
+ "required": false,
14991
+ "type": "object",
14992
+ "in": "body"
14993
+ }
14994
+ ]
14995
+ },
14996
+ "delete-metadata-schema": {
14997
+ serviceClass: "MetadataSchemas",
14998
+ operationId: "deleteMetadataSchema",
14999
+ description: "Removes the declaration. Documents already stored keep the metadata they hold — the rule governed writes, not rows.",
15000
+ moduleDocsUrl: "https://docs.naturali.ai/docs/modules/metadata-schemas",
15001
+ httpMethod: "delete",
15002
+ pathParams: ["project_id", "metadata_schema_id"],
15003
+ queryParams: [],
15004
+ headerParams: [],
15005
+ cookieParams: [],
15006
+ flags: [{
15007
+ "name": "project_id",
15008
+ "description": "Project public ID (proj_ prefix).",
15009
+ "required": true,
15010
+ "type": "string",
15011
+ "in": "path"
15012
+ }, {
15013
+ "name": "metadata_schema_id",
15014
+ "description": "",
15015
+ "required": true,
15016
+ "type": "string",
15017
+ "in": "path"
15018
+ }]
15019
+ },
14030
15020
  "list-model-routes": {
14031
15021
  serviceClass: "ModelRoutes",
14032
15022
  operationId: "listModelRoutes",
@@ -14492,7 +15482,7 @@ const routes = {
14492
15482
  httpMethod: "patch",
14493
15483
  pathParams: ["project_id", "orchestration_id"],
14494
15484
  queryParams: [],
14495
- headerParams: [],
15485
+ headerParams: ["If-Match"],
14496
15486
  cookieParams: [],
14497
15487
  flags: [
14498
15488
  {
@@ -14509,6 +15499,13 @@ const routes = {
14509
15499
  "type": "string",
14510
15500
  "in": "path"
14511
15501
  },
15502
+ {
15503
+ "name": "If-Match",
15504
+ "description": "The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.",
15505
+ "required": false,
15506
+ "type": "string",
15507
+ "in": "header"
15508
+ },
14512
15509
  {
14513
15510
  "name": "name",
14514
15511
  "description": "",
@@ -14557,6 +15554,13 @@ const routes = {
14557
15554
  "required": false,
14558
15555
  "type": "string",
14559
15556
  "in": "body"
15557
+ },
15558
+ {
15559
+ "name": "expected_version",
15560
+ "description": "Refuses the write unless the resource is at this version.",
15561
+ "required": false,
15562
+ "type": "string",
15563
+ "in": "body"
14560
15564
  }
14561
15565
  ]
14562
15566
  },
@@ -14811,7 +15815,7 @@ const routes = {
14811
15815
  },
14812
15816
  {
14813
15817
  "name": "tool_context",
14814
- "description": "Key-value pairs forwarded as `X-Naturali-Context-<key>` headers on every `http` and `mcp` tool call made by an agent node of this run — including the agents of any child run a `loop` or `sub_orchestration` node starts. The header name is `X-Naturali-Context-` plus the key verbatim; no character is re-cased.\n\nThe bag is stored on the run and re-read on every step, so it survives an `awaiting_input` pause, a `sleeping` wait, a background worker drive and a crash redrive. It is **write-only**: a run is a record every principal who may read runs can read, and a credential in it is not theirs to see, so it is never returned on one. A key that is not a valid HTTP header name, or two keys that map to the same header, are rejected with `400 INVALID_TOOL_CONTEXT_KEY` and no run is created.\n\nThe reserved identity keys (`session_id`, `actor_id`, `actor_external_id`) are stripped at generation time — a caller cannot address them from here.",
15818
+ "description": "Key-value pairs forwarded as `X-Naturali-Context-<key>` headers on every `http` and `mcp` tool call made by an agent node of this run — including the agents of any child run a `loop` or `sub_orchestration` node starts. The header name is `X-Naturali-Context-` plus the key verbatim; no character is re-cased.\n\nThe bag is stored on the run and re-read on every step, so it survives an `awaiting_input` pause, a `sleeping` wait, a background worker drive and a crash redrive. It is **write-only**: a run is a record every principal who may read runs can read, and a credential in it is not theirs to see, so it is never returned on one. It also does not outlive the run: reaching a terminal status (`succeeded`, `failed`, `cancelled` or `expired`) clears it. A key that is not a valid HTTP header name, or two keys that map to the same header, are rejected with `400 INVALID_TOOL_CONTEXT_KEY` and no run is created.\n\nThe reserved identity keys (`session_id`, `actor_id`, `actor_external_id`) are stripped at generation time — a caller cannot address them from here.",
14815
15819
  "required": false,
14816
15820
  "type": "object",
14817
15821
  "in": "body"
@@ -14820,7 +15824,7 @@ const routes = {
14820
15824
  "name": "metadata",
14821
15825
  "description": "Caller-supplied key/value metadata attached to the run record for per-run attribution (e.g. which of your own tenants this run belongs to, or the dispatch batch that started it). Round-trips verbatim on every read of the run, on the list as well as the single read.\n\nThe bag is caller-owned and no key is reserved: server-owned state (status, the pinned orchestration version, the trace, usage, artifacts, the run's own `input` and accumulated `state`) lives in its own top-level field and cannot be written from here.\n\nIt is **not** merged into run state: no graph node sees it, and an `input_schema` never has to tolerate it — which is what makes it the place for an infrastructural label, rather than `input`. Keys are never transformed. It is not inherited by the child runs a `loop` or `sub_orchestration` node starts; each child carries whatever the graph gives it, which today is nothing.",
14822
15826
  "required": false,
14823
- "type": "object",
15827
+ "type": "string",
14824
15828
  "in": "body"
14825
15829
  },
14826
15830
  {
@@ -16183,7 +17187,7 @@ const routes = {
16183
17187
  },
16184
17188
  {
16185
17189
  "name": "tool_context",
16186
- "description": "Key-value pairs forwarded as `X-Naturali-Context-<key>` headers on every `http` and `mcp` tool call in this session. The header name is the deployment's configured context prefix (`X-Naturali-Context-` by default) plus the key verbatim — no character is re-cased. Keys are never case-converted — they round-trip exactly as sent. A key that is not a valid HTTP header name, or two keys that map to the same header, are rejected with `400 INVALID_TOOL_CONTEXT_KEY`.",
17190
+ "description": "Key-value pairs forwarded as `X-Naturali-Context-<key>` headers on every `http` and `mcp` tool call in this session. The header name is the deployment's configured context prefix (`X-Naturali-Context-` by default) plus the key verbatim — no character is re-cased. Keys are stored exactly as sent and never case-converted. A key that is not a valid HTTP header name, or two keys that map to the same header, are rejected with `400 INVALID_TOOL_CONTEXT_KEY`. `session_id`, `actor_id` and `actor_external_id` are server-derived and dropped from the stored bag, in any casing. Write-only: no read of a session returns it. It also does not outlive the session: closing or expiring it clears the bag.",
16187
17191
  "required": false,
16188
17192
  "type": "object",
16189
17193
  "in": "body"
@@ -16276,7 +17280,7 @@ const routes = {
16276
17280
  },
16277
17281
  {
16278
17282
  "name": "tool_context",
16279
- "description": "Key-value pairs forwarded as `X-Naturali-Context-<key>` headers on every `http` and `mcp` tool call in this session. The header name is the deployment's configured context prefix (`X-Naturali-Context-` by default) plus the key verbatim — no character is re-cased. Keys are never case-converted — they round-trip exactly as sent. A key that is not a valid HTTP header name, or two keys that map to the same header, are rejected with `400 INVALID_TOOL_CONTEXT_KEY`.",
17283
+ "description": "Key-value pairs forwarded as `X-Naturali-Context-<key>` headers on every `http` and `mcp` tool call in this session. The header name is the deployment's configured context prefix (`X-Naturali-Context-` by default) plus the key verbatim — no character is re-cased. Keys are stored exactly as sent and never case-converted. A key that is not a valid HTTP header name, or two keys that map to the same header, are rejected with `400 INVALID_TOOL_CONTEXT_KEY`. `session_id`, `actor_id` and `actor_external_id` are server-derived and dropped from the stored bag, in any casing. Write-only: no read of a session returns it. It also does not outlive the session: closing or expiring it clears the bag.",
16280
17284
  "required": false,
16281
17285
  "type": "object",
16282
17286
  "in": "body"
@@ -16513,7 +17517,7 @@ const routes = {
16513
17517
  },
16514
17518
  {
16515
17519
  "name": "tags",
16516
- "description": "Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter and knowledge search.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.",
17520
+ "description": "Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with `400 VALIDATION_FAILED`, never coerced. Keys are opaque and stored verbatim, so `cost_center` and `costCenter` are two different tags. Matched by JSONB containment wherever tags are read: the `?tags=` filter and knowledge search.\n\nKeys beginning `system.` are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with `400 RESERVED_TAG_KEY`. They are read and filtered like any other tag.\n\nThe bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is `400 VALIDATION_FAILED` with `meta.limit` naming the bound it crossed. `system.*` keys are the platform's and do not count against the 50.",
16517
17521
  "required": false,
16518
17522
  "type": "object",
16519
17523
  "in": "body"
@@ -16781,7 +17785,7 @@ const routes = {
16781
17785
  "name": "metadata",
16782
17786
  "description": "Caller-supplied key/value metadata attached to the task record for attribution — which of your own tenants the task belongs to, the ticket that raised it, the import batch that created it. Round-trips verbatim on every read of the task, the list included, and survives every transition (a transition supplies no metadata of its own).\n\nThe bag is caller-owned and no key is reserved: everything the engine decides about a task (`state`, `status`, `workflow_version`, `last_result`, `active_dispatch`, the automation fields) is a field of its own and cannot be written from here.\n\nPrefer this over `payload` for anything that is not task data: `payload` is read by every guard as `task.payload` and may be written by the workflow's declared `payload_writes`, so a label parked there is neither invisible to the state machine nor safe from it. A non-object is rejected with `400 VALIDATION_FAILED` and no task is created.",
16783
17787
  "required": false,
16784
- "type": "object",
17788
+ "type": "string",
16785
17789
  "in": "body"
16786
17790
  }
16787
17791
  ]
@@ -18454,7 +19458,7 @@ const routes = {
18454
19458
  httpMethod: "patch",
18455
19459
  pathParams: ["project_id", "workflow_id"],
18456
19460
  queryParams: [],
18457
- headerParams: [],
19461
+ headerParams: ["If-Match"],
18458
19462
  cookieParams: [],
18459
19463
  flags: [
18460
19464
  {
@@ -18471,6 +19475,13 @@ const routes = {
18471
19475
  "type": "string",
18472
19476
  "in": "path"
18473
19477
  },
19478
+ {
19479
+ "name": "If-Match",
19480
+ "description": "The version the caller believes the resource holds, as an entity tag (`3` or `\"3\"`). Equivalent to `expected_version` in the request body; `*` states no precondition beyond the resource existing. A mismatch is `409 VERSION_CONFLICT`.",
19481
+ "required": false,
19482
+ "type": "string",
19483
+ "in": "header"
19484
+ },
18474
19485
  {
18475
19486
  "name": "name",
18476
19487
  "description": "",
@@ -18512,6 +19523,13 @@ const routes = {
18512
19523
  "required": false,
18513
19524
  "type": "string",
18514
19525
  "in": "body"
19526
+ },
19527
+ {
19528
+ "name": "expected_version",
19529
+ "description": "Refuses the write unless the resource is at this version.",
19530
+ "required": false,
19531
+ "type": "string",
19532
+ "in": "body"
18515
19533
  }
18516
19534
  ]
18517
19535
  },