n8n-nodes-lifespace 0.1.7 → 0.1.8

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 (38) hide show
  1. package/README.md +51 -30
  2. package/dist/nodes/LifeSpace/LifeSpace.node.d.ts +1 -0
  3. package/dist/nodes/LifeSpace/LifeSpace.node.js +109 -64
  4. package/dist/nodes/LifeSpace/LifeSpace.node.js.map +1 -1
  5. package/dist/nodes/LifeSpaceAgentTool/LifeSpaceAgentTool.node.d.ts +6 -0
  6. package/dist/nodes/LifeSpaceAgentTool/LifeSpaceAgentTool.node.js +99 -0
  7. package/dist/nodes/LifeSpaceAgentTool/LifeSpaceAgentTool.node.js.map +1 -0
  8. package/dist/nodes/LifeSpaceAgentTool/lifespace.dark.svg +14 -0
  9. package/dist/nodes/LifeSpaceAgentTool/lifespace.svg +14 -0
  10. package/dist/nodes/LifeSpaceWorkflow/LifeSpaceWorkflow.node.d.ts +6 -0
  11. package/dist/nodes/LifeSpaceWorkflow/LifeSpaceWorkflow.node.js +139 -0
  12. package/dist/nodes/LifeSpaceWorkflow/LifeSpaceWorkflow.node.js.map +1 -0
  13. package/dist/nodes/LifeSpaceWorkflow/humanProjection.d.ts +10 -0
  14. package/dist/nodes/LifeSpaceWorkflow/humanProjection.js +167 -0
  15. package/dist/nodes/LifeSpaceWorkflow/humanProjection.js.map +1 -0
  16. package/dist/nodes/LifeSpaceWorkflow/lifespace.dark.svg +14 -0
  17. package/dist/nodes/LifeSpaceWorkflow/lifespace.svg +14 -0
  18. package/dist/nodes/agent/LifeSpaceAgentToolBase.d.ts +1 -0
  19. package/dist/nodes/agent/LifeSpaceAgentToolBase.js +6 -0
  20. package/dist/nodes/agent/LifeSpaceAgentToolBase.js.map +1 -0
  21. package/dist/nodes/agent/lifeSpaceGenericQueryTool.d.ts +24 -0
  22. package/dist/nodes/agent/lifeSpaceGenericQueryTool.js +197 -0
  23. package/dist/nodes/agent/lifeSpaceGenericQueryTool.js.map +1 -0
  24. package/dist/nodes/agent/lifeSpaceToolTypes.d.ts +10 -0
  25. package/dist/nodes/agent/lifeSpaceToolTypes.js +3 -0
  26. package/dist/nodes/agent/lifeSpaceToolTypes.js.map +1 -0
  27. package/dist/nodes/shared/lifeSpaceAdapterSemantics.d.ts +8 -0
  28. package/dist/nodes/shared/lifeSpaceAdapterSemantics.js +28 -0
  29. package/dist/nodes/shared/lifeSpaceAdapterSemantics.js.map +1 -0
  30. package/dist/nodes/shared/lifeSpaceLocalDateSemantics.d.ts +9 -0
  31. package/dist/nodes/shared/lifeSpaceLocalDateSemantics.js +23 -0
  32. package/dist/nodes/shared/lifeSpaceLocalDateSemantics.js.map +1 -0
  33. package/dist/nodes/shared/lifeSpaceQuerySemantics.d.ts +14 -0
  34. package/dist/nodes/shared/lifeSpaceQuerySemantics.js +120 -0
  35. package/dist/nodes/shared/lifeSpaceQuerySemantics.js.map +1 -0
  36. package/dist/package.json +3 -3
  37. package/dist/tsconfig.tsbuildinfo +1 -1
  38. package/package.json +3 -3
package/README.md CHANGED
@@ -66,7 +66,12 @@ Runtime Discovery determines which Spaces, Record Types, fields, queries, Action
66
66
 
67
67
  ## Generated Record UX
68
68
 
69
- Create/Update keep scalar fields in n8n Resource Mapper while using native n8n controls for LifeSpace calendar-date fields and supported relations. Single relations use a selector; multi-relations use multi-select. List / Query offers typed filter variants for text, enum, boolean, number, date/time and authorized relations, while retaining the raw legacy filter as an expression/compatibility escape hatch.
69
+ The package deliberately exposes two different projections over the same LifeSpace Runtime Discovery semantics:
70
+
71
+ - **LifeSpace** is the human-authored workflow node. Create/Update fields are generated through n8n Resource Mapper from the selected Record Type, so field type, required state, enum values, calendar dates and supported single relations determine the control automatically. List / Query exposes semantic predicates such as `Status — Is One Of` or `Due Date — After or Equal` instead of asking the workflow author to choose a filter type first. Multi-value relations keep their dedicated multi-select UX.
72
+ - **LifeSpace Agent Tool** is the native AI Tool surface. Its schema is generated for the model: generic queries expose semantic `field` / `operator` / `value` inputs, and the adapter compiles those inputs to the exact transport names published by LifeSpace. Capability Queries consume the grouped schema published by Runtime Discovery.
73
+
74
+ The human workflow node is not exposed through `usableAsTool`; this avoids maintaining two competing Agent Tool surfaces with different schema behavior.
70
75
 
71
76
  The node displays the authorized human-readable `spaceName` when present while continuing to submit the stable `spc_*` ID.
72
77
 
@@ -74,16 +79,6 @@ Record Type is the LifeSpace `modelKey` (for example `task`). Design-time option
74
79
 
75
80
  Calendar-backed models use canonical `capabilityBindings.calendar` field roles instead of Event-specific field names while the node is being configured. Date-only values are normalized to `YYYY-MM-DD`. Create/Update execution does not fetch fresh semantic Discovery solely to produce an adapter-local Calendar conflict error; the canonical mutation goes directly to LifeSpace Core, which remains authoritative for Calendar validation and current authorization.
76
81
 
77
- ## AI Agent Tool
78
-
79
- `LifeSpace Tool` is the metadata-driven AI Agent sub-node. Configure one fixed Space, Record Type and semantic operation per Tool instance, then connect multiple instances to the same n8n AI Agent. The Tool name, default description and structured AI input schema are generated from LifeSpace Progressive Runtime Discovery; hand-written descriptions are optional.
80
-
81
- - Query exposes published search/filter/comparison/local-date-window/sort/pagination semantics. Capability Query mode exposes only the capability-owned parameters and ordering that LifeSpace explicitly publishes; generic-facet composition stays narrowed until LifeSpace #228 defines it.
82
- - Create/Update schemas come from writable Model fields. Required fields with LifeSpace defaults are optional AI inputs, and fields the AI omits are absent from the outgoing request rather than synthesized as empty/null placeholders.
83
- - Delete and Action hide optimistic-concurrency `version` from the AI. The Adapter reads the current record version when required and Core remains the final authorization/concurrency/semantic authority.
84
- - No Task/Event/Wish-specific Tool nodes or field maps are shipped. New models become available through Discovery without source changes.
85
- - The ordinary `LifeSpace` workflow node remains `usableAsTool` for manually configured `$fromAI(...)` workflows, but `LifeSpace Tool` is the canonical path when the AI should receive the complete dynamic LifeSpace schema.
86
-
87
82
  ## LifeSpace contract compatibility
88
83
 
89
84
  This package follows the current LifeSpace Core Kernel `0.35.0` contract family. It consumes Runtime/Discovery semantics only; Eventing configuration and webhook delivery semantics are owned independently by Integration/Eventing `0.1.0`.
@@ -140,16 +135,16 @@ Examples:
140
135
  {{$vars.lifeSpaceRecordType}}
141
136
  ```
142
137
 
143
- Discovery-backed selectors such as **Space**, **Filter Field**, **Sort Field** and **Action** support the normal n8n pattern: choose a value from the list, or switch the parameter to an expression and provide the corresponding stable ID/key. **Record Type** stores the plain LifeSpace `modelKey`, so a LifeSpace Trigger can feed a Record node directly without a mapping step or an extra Discovery request.
138
+ Discovery-backed selectors such as **Space**, **Sort Field** and **Action** support the normal n8n pattern: choose a value from the list, or switch the parameter to an expression and provide the corresponding stable ID/key. **Record Type** stores the plain LifeSpace `modelKey`, so a LifeSpace Trigger can feed a Record node directly without a mapping step or an extra Discovery request.
144
139
 
145
- The same applies to ordinary values such as Record ID, Search, Filter Value, Return All, Limit, Sort Direction, Cursor, explicit Version, API Method, API Path and JSON Body.
140
+ The same applies to ordinary values such as Record ID, Search, Return All, Limit, Sort Direction, Cursor, explicit Version, API Method, API Path and JSON Body.
146
141
 
147
142
  Two boundaries are intentional:
148
143
 
149
144
  - **Resource** and **Operation** are structural node controls and do not accept expressions because they determine which parameter schema and execution path the node has.
150
- - **Fields** and **Action Input** use n8n's `resourceMapper`. The mapper container is structural, but each generated field value inside it remains expression-capable. This includes relation-backed field values: the UI can offer authorized options while an expression can still supply a stable relation ID or ID list.
145
+ - **Fields**, **Filters**, Capability Query parameters and **Action Input** are generated from Runtime Discovery. Their mapper containers are structural, while generated values remain expression-capable.
151
146
 
152
- For **Filters** and **Sorts**, add the required rows in the node UI and use expressions inside each row's Field/Operator/Value or Field/Direction inputs. The number of rows is treated as workflow structure rather than per-item data. This avoids relying on whole-array expressions for n8n `fixedCollection` parameters.
147
+ In Standard Query, **Filters** is a Discovery-driven Resource Mapper. Adding a filter selects a published semantic predicate such as `Due Date — Before`; n8n then renders the value control from the underlying field type. **Sorts** remains an ordered structural list because sort priority is part of workflow structure rather than per-item data.
153
148
 
154
149
  If **Record Type** itself varies per input item and those Record Types have different schemas, one discovery-generated mapper cannot safely represent every possible schema at design time. In that case, branch to separate LifeSpace nodes per schema or use **API Request** for a deliberately fully dynamic request.
155
150
 
@@ -188,7 +183,7 @@ LifeSpace server defaults are authoritative. A field that is `required` but has
188
183
 
189
184
  For example, lifecycle state such as Task status can remain server/Action-owned instead of being manually entered by the workflow author.
190
185
 
191
- When Runtime Discovery advertises relation semantics, supported `person`, `person_list`, `record` and `record_list` fields are rendered from the current authorized `{ id, label }` target projection instead of asking the workflow author to type raw identifiers. The displayed value is the canonical LifeSpace reference label, while the workflow payload still stores and submits the stable target ID.
186
+ When Runtime Discovery advertises relation semantics, supported single-value `person` / `record` fields are rendered as selectors from the current authorized `{ id, label }` target projection. Multi-value relations retain the dedicated multi-select control rather than falling back to n8n Resource Mapper's generic JSON array editor. The displayed value is the canonical LifeSpace reference label, while the workflow payload still stores and submits the stable target ID.
192
187
 
193
188
  Older compatible Discovery responses without relation lookup metadata retain the raw-ID field behavior. The adapter never guesses record labels from conventional fields such as `name`, `title` or `summary`.
194
189
 
@@ -208,19 +203,18 @@ If a workflow intentionally needs to bind a known version, add **Concurrency Opt
208
203
 
209
204
  ### List / Query
210
205
 
211
- The normal UI supports:
206
+ **Query Type** separates ordinary record query UX from capability-owned query semantics:
207
+
208
+ - **Records** is the normal query surface and exposes optional **Search**, Discovery-driven semantic **Filters**, optional **Advanced Local Date Windows**, generic **Sorts**, **Return All** and **Limit**;
209
+ - each Filter entry represents an allowed field/operator pair published by LifeSpace, for example `Status — Is One Of`, `Due Date — Before` or `Created At — After or Equal`; the adapter compiles the selected semantic predicate to the exact transport parameter published by Discovery;
210
+ - **Capability Query** is offered only when the selected Record Type publishes `query.capabilityQueries`; its selector, parameters and ordering are loaded from Runtime Discovery;
211
+ - **Return All** follows `nextCursor` automatically, while **Limit** bounds a single-page query.
212
212
 
213
- - optional **Search**;
214
- - one or more typed **Filters**;
215
- - Discovery-driven explicit **Number Comparison** and **Date / Time Comparison** rows using the exact LifeSpace-published operator transport;
216
- - generic **Local Date Windows** for datetime fields advertised by Time Semantics, including envelope `createdAt` / `updatedAt`, with local dates + IANA timezone sent unchanged to Core;
217
- - optional grouped **Semantic Query** input generated from `query.capabilityQueries` (for example `calendar.window`) plus its published semantic ordering;
218
- - **Return All** to follow `nextCursor` automatically;
219
- - **Limit** when Return All is disabled.
213
+ Advanced Local Date Windows are shown separately because a local calendar window is a structured field/start/end/timezone input rather than a scalar predicate. The adapter sends the published date-window parameter names and IANA timezone unchanged; Core owns timezone/DST conversion. Existing persisted legacy `exact/from/to` filters preserve the inclusive `To` behavior.
220
214
 
221
- The adapter never derives explicit comparison parameter names from field naming and never converts local calendar windows to UTC. Those transport names and timezone/DST semantics come from LifeSpace Runtime Semantic Detail. Existing `exact/from/to` filters remain available as compatibility UI and preserve the legacy inclusive `To` behavior.
215
+ Generic Search/Filters/Sorts are intentionally hidden in Capability Query mode until LifeSpace publishes machine-readable facet-composability metadata. This prevents the adapter from guessing which generic parameters may be mixed with a grouped capability query.
222
216
 
223
- Use **Sorts → Add Sort** to add zero or more sort criteria in priority order. Sortable model fields use authoritative `title` metadata from Runtime Discovery, while envelope fields such as `createdAt` / `updatedAt` are offered only when Discovery advertises them. Multiple criteria are sent as ordered repeated `sort=field:direction` query parameters.
217
+ Use **Sorts → Add Sort** in Records mode to add zero or more sort criteria in priority order. Sortable model fields use authoritative `title` metadata from Runtime Discovery, while envelope fields such as `createdAt` / `updatedAt` are offered only when Discovery advertises them. Multiple criteria are sent as ordered repeated `sort=field:direction` query parameters.
224
218
 
225
219
  Advanced **Options** contain manual **Cursor** as an escape hatch.
226
220
 
@@ -248,6 +242,30 @@ Paths are relative to the configured API Base URL, for example:
248
242
 
249
243
  Use normal Record operations when possible because they benefit from Runtime Discovery metadata and n8n-specific UX.
250
244
 
245
+ ## Use the LifeSpace Agent Tool
246
+
247
+ Use **LifeSpace Agent Tool** when connecting LifeSpace to an n8n AI Agent. This is a separate native AiTool surface rather than the human workflow node running through `usableAsTool`.
248
+
249
+ Configure the Tool's structural scope — Space, Record Type and operation — in the node. The Tool then derives its model-facing name, description and input schema from Runtime Discovery, so multiple LifeSpace Tools can distinguish their configured purpose without requiring handwritten descriptions.
250
+
251
+ For Generic Query, the model receives semantic inputs such as:
252
+
253
+ ```json
254
+ {
255
+ "search": "food",
256
+ "filters": [
257
+ { "field": "status", "operator": "in", "value": ["todo"] },
258
+ { "field": "dueDate", "operator": "gte", "value": "2026-09-13" }
259
+ ],
260
+ "sort": [{ "field": "dueDate", "direction": "asc" }],
261
+ "limit": 20
262
+ }
263
+ ```
264
+
265
+ The adapter validates those field/operator pairs against Discovery and compiles them to the exact LifeSpace query transport. The model does not need to know transport names such as `dueDate.gte`. Local date windows are likewise semantic inputs and Core remains responsible for timezone/DST conversion.
266
+
267
+ Create/Update schemas are generated from writable model fields. Optional fields that the model does not provide are omitted rather than synthesized as empty values. Actions and Capability Queries remain metadata-driven, and adding a future Record Type does not require a model-specific `create_*` or `query_*` node implementation.
268
+
251
269
  ## Use the LifeSpace Trigger
252
270
 
253
271
  The **LifeSpace Trigger** receives signed LifeSpace Domain Event webhooks.
@@ -283,11 +301,12 @@ Webhook Endpoint / Event Subscription creation is intentionally not performed by
283
301
 
284
302
  - **LifeSpace API** credential for API authentication and Runtime Discovery;
285
303
  - **LifeSpace Webhook Signing** credential for endpoint-scoped inbound HMAC verification;
286
- - **LifeSpace** node with discovery-driven Record operations plus advanced API Request;
304
+ - **LifeSpace** human workflow node with Discovery-driven Record operations plus advanced API Request;
305
+ - **LifeSpace Agent Tool** native AiTool with Discovery-driven semantic schemas;
287
306
  - **LifeSpace Trigger** with signed multi-Record-Type Domain Event filtering;
288
- - dynamic Space, Record Type, field, relation target, query, Action and Action Input UI based on progressive Runtime Discovery.
307
+ - shared thin adapter projection from LifeSpace Runtime Discovery into human n8n controls and model-facing Tool schemas.
289
308
 
290
- LifeSpace remains authoritative for validation, authorization, defaults, Mutation Authority, Action semantics, relation semantics and event contracts. Runtime Discovery and Relation Target Lookup are current capability/reference projections, not execution-authorization proofs.
309
+ LifeSpace remains authoritative for validation, authorization, defaults, Mutation Authority, Action semantics, query/time semantics, relation semantics and event contracts. Runtime Discovery and Relation Target Lookup are current capability/reference projections, not execution-authorization proofs.
291
310
 
292
311
  ## Architecture boundary
293
312
 
@@ -295,6 +314,8 @@ LifeSpace is the source of truth for Identity, Authority, Shared Reality semanti
295
314
 
296
315
  LifeSpace does not depend on n8n. n8n is one optional orchestration/integration runtime consuming LifeSpace APIs and events.
297
316
 
317
+ The human Workflow node and native Agent Tool are separate presentation projections over the same LifeSpace semantics. Shared code in this package only translates Discovery into n8n-specific UI/schema/transport; it does not redefine domain rules.
318
+
298
319
  The adapter does not use privileged model-admin endpoints for ordinary workflow discovery and does not copy Model Definitions into this repository.
299
320
 
300
321
  ## Development
@@ -337,4 +358,4 @@ The adapter now consumes generic `person`, `person_list`, `record` and `record_l
337
358
 
338
359
  ## License
339
360
 
340
- MIT
361
+ MIT
@@ -20,6 +20,7 @@ export declare class LifeSpace implements INodeType {
20
20
  getTemporalComparisonFields(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
21
21
  getComparisonOperatorsForCurrentField(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
22
22
  getLocalDateWindowFields(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
23
+ getQueryModes(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
23
24
  getCapabilityQueries(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
24
25
  getSemanticSorts(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
25
26
  getPersonFilterableFields(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
@@ -171,26 +171,40 @@ function normalizeActionInput(context, itemIndex, value, fields) {
171
171
  return result;
172
172
  }
173
173
  function queryParameters(context, itemIndex, limit, cursorOverride) {
174
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5;
174
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8;
175
175
  const qs = {};
176
- const search = String(context.getNodeParameter('search', itemIndex, '')).trim();
176
+ const configuredQueryMode = String((_a = context.getNodeParameter('queryMode', itemIndex, '')) !== null && _a !== void 0 ? _a : '').trim();
177
+ const legacyCapabilityQueryKey = String((_b = context.getNodeParameter('semanticQueryKey', itemIndex, '')) !== null && _b !== void 0 ? _b : '').trim();
178
+ const rawCapabilityQueryInput = mappedValue(context, itemIndex, 'semanticQueryInput');
179
+ const rawCapabilitySort = String((_c = context.getNodeParameter('semanticSort', itemIndex, '')) !== null && _c !== void 0 ? _c : '').trim();
180
+ const hasLegacyCapabilityConfiguration = legacyCapabilityQueryKey !== ''
181
+ || Object.keys(rawCapabilityQueryInput).length > 0
182
+ || rawCapabilitySort !== '';
183
+ const queryMode = configuredQueryMode || (hasLegacyCapabilityConfiguration ? 'capability' : 'standard');
184
+ const useStandardQuery = queryMode !== 'capability';
185
+ const useCapabilityQuery = queryMode === 'capability';
186
+ const search = useStandardQuery ? String(context.getNodeParameter('search', itemIndex, '')).trim() : '';
177
187
  const options = context.getNodeParameter('options', itemIndex, {});
178
- const configuredSorts = context.getNodeParameter('sorts.sort', itemIndex, []);
179
- const semanticSort = String((_a = context.getNodeParameter('semanticSort', itemIndex, '')) !== null && _a !== void 0 ? _a : '').trim();
180
- const legacySortField = String((_b = options.sortField) !== null && _b !== void 0 ? _b : '').trim();
181
- const legacySortDirection = String((_c = options.sortDirection) !== null && _c !== void 0 ? _c : 'desc').trim();
182
- const configuredCursor = String((_d = options.cursor) !== null && _d !== void 0 ? _d : '').trim();
188
+ const configuredSorts = useStandardQuery
189
+ ? context.getNodeParameter('sorts.sort', itemIndex, [])
190
+ : [];
191
+ const semanticSort = useCapabilityQuery ? rawCapabilitySort : '';
192
+ const legacySortField = useStandardQuery ? String((_d = options.sortField) !== null && _d !== void 0 ? _d : '').trim() : '';
193
+ const legacySortDirection = String((_e = options.sortDirection) !== null && _e !== void 0 ? _e : 'desc').trim();
194
+ const configuredCursor = String((_f = options.cursor) !== null && _f !== void 0 ? _f : '').trim();
183
195
  const cursor = cursorOverride !== null && cursorOverride !== void 0 ? cursorOverride : configuredCursor;
184
- const filters = context.getNodeParameter('filters.filter', itemIndex, []);
196
+ const filters = useStandardQuery
197
+ ? context.getNodeParameter('filters.filter', itemIndex, [])
198
+ : [];
185
199
  if (search)
186
200
  qs.q = search;
187
201
  const orderedSorts = [];
188
202
  const usedSortFields = new Set();
189
203
  for (const sort of configuredSorts) {
190
- const field = String((_e = sort.field) !== null && _e !== void 0 ? _e : '').trim();
204
+ const field = String((_g = sort.field) !== null && _g !== void 0 ? _g : '').trim();
191
205
  if (!field)
192
206
  continue;
193
- const direction = String((_f = sort.direction) !== null && _f !== void 0 ? _f : 'asc').trim();
207
+ const direction = String((_h = sort.direction) !== null && _h !== void 0 ? _h : 'asc').trim();
194
208
  if (direction !== 'asc' && direction !== 'desc') {
195
209
  throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Sort direction for ${field} must be asc or desc`, { itemIndex });
196
210
  }
@@ -227,37 +241,39 @@ function queryParameters(context, itemIndex, limit, cursorOverride) {
227
241
  setQueryParameter(key, value);
228
242
  };
229
243
  for (const filter of filters) {
230
- const field = String((_g = filter.field) !== null && _g !== void 0 ? _g : '').trim();
231
- const value = String((_h = filter.value) !== null && _h !== void 0 ? _h : '');
232
- const operator = (_j = filter.operator) !== null && _j !== void 0 ? _j : 'exact';
244
+ const field = String((_j = filter.field) !== null && _j !== void 0 ? _j : '').trim();
245
+ const value = String((_k = filter.value) !== null && _k !== void 0 ? _k : '');
246
+ const operator = (_l = filter.operator) !== null && _l !== void 0 ? _l : 'exact';
233
247
  if (field && value !== '')
234
248
  setFilter(field, operator, value);
235
249
  }
236
- const typedFilters = context.getNodeParameter('filters', itemIndex, {});
237
- for (const row of ((_k = typedFilters.text) !== null && _k !== void 0 ? _k : [])) {
238
- const field = String((_l = row.field) !== null && _l !== void 0 ? _l : '').trim();
250
+ const typedFilters = useStandardQuery
251
+ ? context.getNodeParameter('filters', itemIndex, {})
252
+ : {};
253
+ for (const row of ((_m = typedFilters.text) !== null && _m !== void 0 ? _m : [])) {
254
+ const field = String((_o = row.field) !== null && _o !== void 0 ? _o : '').trim();
239
255
  if (field && row.value !== undefined && row.value !== '')
240
256
  setFilter(field, 'exact', String(row.value));
241
257
  }
242
- for (const row of ((_m = typedFilters.enum) !== null && _m !== void 0 ? _m : [])) {
243
- const field = String((_o = row.field) !== null && _o !== void 0 ? _o : '').trim();
258
+ for (const row of ((_p = typedFilters.enum) !== null && _p !== void 0 ? _p : [])) {
259
+ const field = String((_q = row.field) !== null && _q !== void 0 ? _q : '').trim();
244
260
  const values = Array.isArray(row.values) ? row.values.map(String).filter(Boolean) : [];
245
261
  if (field && values.length)
246
262
  setFilter(field, 'exact', values.join(','));
247
263
  }
248
- for (const row of ((_p = typedFilters.boolean) !== null && _p !== void 0 ? _p : [])) {
249
- const field = String((_q = row.field) !== null && _q !== void 0 ? _q : '').trim();
264
+ for (const row of ((_r = typedFilters.boolean) !== null && _r !== void 0 ? _r : [])) {
265
+ const field = String((_s = row.field) !== null && _s !== void 0 ? _s : '').trim();
250
266
  if (field)
251
267
  setFilter(field, 'exact', Boolean(row.value));
252
268
  }
253
- for (const row of ((_r = typedFilters.number) !== null && _r !== void 0 ? _r : [])) {
254
- const field = String((_s = row.field) !== null && _s !== void 0 ? _s : '').trim();
269
+ for (const row of ((_t = typedFilters.number) !== null && _t !== void 0 ? _t : [])) {
270
+ const field = String((_u = row.field) !== null && _u !== void 0 ? _u : '').trim();
255
271
  const value = Number(row.value);
256
272
  if (field && Number.isFinite(value))
257
- setFilter(field, (_t = row.operator) !== null && _t !== void 0 ? _t : 'exact', value);
273
+ setFilter(field, (_v = row.operator) !== null && _v !== void 0 ? _v : 'exact', value);
258
274
  }
259
- for (const row of ((_u = typedFilters.temporal) !== null && _u !== void 0 ? _u : [])) {
260
- const encoded = String((_v = row.field) !== null && _v !== void 0 ? _v : '').trim();
275
+ for (const row of ((_w = typedFilters.temporal) !== null && _w !== void 0 ? _w : [])) {
276
+ const encoded = String((_x = row.field) !== null && _x !== void 0 ? _x : '').trim();
261
277
  if (!encoded || row.value === undefined || row.value === '')
262
278
  continue;
263
279
  const separator = encoded.indexOf(':');
@@ -266,24 +282,24 @@ function queryParameters(context, itemIndex, limit, cursorOverride) {
266
282
  const raw = String(row.value);
267
283
  const value = fieldType === 'date' ? dateOnlyValue(context, itemIndex, raw, field) : raw;
268
284
  if (value !== null)
269
- setFilter(field, (_w = row.operator) !== null && _w !== void 0 ? _w : 'exact', value);
285
+ setFilter(field, (_y = row.operator) !== null && _y !== void 0 ? _y : 'exact', value);
270
286
  }
271
- for (const row of ((_x = typedFilters.person) !== null && _x !== void 0 ? _x : [])) {
272
- const field = String((_y = row.field) !== null && _y !== void 0 ? _y : '').trim();
273
- const target = String((_z = row.target) !== null && _z !== void 0 ? _z : '').trim();
287
+ for (const row of ((_z = typedFilters.person) !== null && _z !== void 0 ? _z : [])) {
288
+ const field = String((_0 = row.field) !== null && _0 !== void 0 ? _0 : '').trim();
289
+ const target = String((_1 = row.target) !== null && _1 !== void 0 ? _1 : '').trim();
274
290
  if (field && target)
275
291
  setFilter(field, 'exact', target);
276
292
  }
277
- for (const row of ((_0 = typedFilters.numberComparison) !== null && _0 !== void 0 ? _0 : [])) {
293
+ for (const row of ((_2 = typedFilters.numberComparison) !== null && _2 !== void 0 ? _2 : [])) {
278
294
  const field = comparisonFieldParts(row.field).field;
279
- const parameter = String((_1 = row.parameter) !== null && _1 !== void 0 ? _1 : '').trim();
295
+ const parameter = String((_3 = row.parameter) !== null && _3 !== void 0 ? _3 : '').trim();
280
296
  const value = Number(row.value);
281
297
  if (field && parameter && Number.isFinite(value))
282
298
  setQueryParameter(parameter, value);
283
299
  }
284
- for (const row of ((_2 = typedFilters.temporalComparison) !== null && _2 !== void 0 ? _2 : [])) {
300
+ for (const row of ((_4 = typedFilters.temporalComparison) !== null && _4 !== void 0 ? _4 : [])) {
285
301
  const { field, valueType } = comparisonFieldParts(row.field);
286
- const parameter = String((_3 = row.parameter) !== null && _3 !== void 0 ? _3 : '').trim();
302
+ const parameter = String((_5 = row.parameter) !== null && _5 !== void 0 ? _5 : '').trim();
287
303
  if (!field || !parameter || row.value === undefined || row.value === '')
288
304
  continue;
289
305
  const raw = String(row.value);
@@ -291,18 +307,23 @@ function queryParameters(context, itemIndex, limit, cursorOverride) {
291
307
  if (value !== null)
292
308
  setQueryParameter(parameter, value);
293
309
  }
294
- const localDateWindows = context.getNodeParameter('localDateWindows.window', itemIndex, []);
310
+ const localDateWindows = useStandardQuery
311
+ ? [
312
+ ...((_6 = typedFilters.localDateWindow) !== null && _6 !== void 0 ? _6 : []),
313
+ ...context.getNodeParameter('localDateWindows.window', itemIndex, []),
314
+ ]
315
+ : [];
295
316
  for (const row of localDateWindows) {
296
317
  const selector = decodeLocalDateWindowSelector(row.field);
297
318
  if (!selector) {
298
- if (String((_4 = row.field) !== null && _4 !== void 0 ? _4 : '').trim()) {
319
+ if (String((_7 = row.field) !== null && _7 !== void 0 ? _7 : '').trim()) {
299
320
  throw new n8n_workflow_1.NodeOperationError(context.getNode(), 'Local Date Window selector is invalid. Re-select the field from Runtime Discovery.', { itemIndex });
300
321
  }
301
322
  continue;
302
323
  }
303
324
  const dateStart = dateOnlyValue(context, itemIndex, row.dateStart, selector.field);
304
325
  const dateEndExclusive = dateOnlyValue(context, itemIndex, row.dateEndExclusive, selector.field);
305
- const timezone = String((_5 = row.timezone) !== null && _5 !== void 0 ? _5 : '').trim();
326
+ const timezone = String((_8 = row.timezone) !== null && _8 !== void 0 ? _8 : '').trim();
306
327
  if (dateStart !== null)
307
328
  setQueryParameter(selector.dateStartParameter, dateStart);
308
329
  if (dateEndExclusive !== null)
@@ -310,12 +331,12 @@ function queryParameters(context, itemIndex, limit, cursorOverride) {
310
331
  if (timezone)
311
332
  setQueryParameter(selector.timezoneParameter, timezone);
312
333
  }
313
- const semanticQueryInput = mappedValue(context, itemIndex, 'semanticQueryInput');
334
+ const semanticQueryInput = useCapabilityQuery ? rawCapabilityQueryInput : {};
314
335
  for (const [parameter, value] of Object.entries(semanticQueryInput)) {
315
336
  if (value === null || value === undefined || value === '')
316
337
  continue;
317
338
  if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean') {
318
- throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Semantic Query parameter ${parameter} must be a scalar value`, { itemIndex });
339
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Capability Query parameter ${parameter} must be a scalar value`, { itemIndex });
319
340
  }
320
341
  setQueryParameter(parameter, value);
321
342
  }
@@ -647,14 +668,21 @@ class LifeSpace {
647
668
  ] }],
648
669
  },
649
670
  {
650
- displayName: 'Search', name: 'search', type: 'string', default: '',
671
+ displayName: 'Query Mode Name or ID', name: 'queryMode', type: 'options', noDataExpression: true,
672
+ typeOptions: { loadOptionsMethod: 'getQueryModes', loadOptionsDependsOn: ['spaceId', 'recordType'] },
673
+ options: [], default: 'standard',
651
674
  displayOptions: { show: { resource: ['modelRecord'], operation: ['list'] } },
675
+ description: 'Standard Query is always available. Capability Query is offered only when the selected Record Type publishes a grouped capability query through Runtime Discovery. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
676
+ },
677
+ {
678
+ displayName: 'Search', name: 'search', type: 'string', default: '',
679
+ displayOptions: { show: { resource: ['modelRecord'], operation: ['list'], queryMode: ['standard'] } },
652
680
  description: 'Full-text search across fields declared searchable by LifeSpace. Leave empty to disable search.',
653
681
  },
654
682
  {
655
683
  displayName: 'Filters', name: 'filters', type: 'fixedCollection', default: {},
656
684
  placeholder: 'Add Filter', typeOptions: { multipleValues: true },
657
- displayOptions: { show: { resource: ['modelRecord'], operation: ['list'] } },
685
+ displayOptions: { show: { resource: ['modelRecord'], operation: ['list'], queryMode: ['standard'] } },
658
686
  options: [
659
687
  { displayName: 'Text Filter', name: 'text', values: [
660
688
  { displayName: 'Field Name or ID', name: 'field', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getTextFilterableFields', loadOptionsDependsOn: ['spaceId', 'recordType'] }, options: [], default: '', required: true },
@@ -688,6 +716,12 @@ class LifeSpace {
688
716
  { displayName: 'Operator Name or ID', name: 'parameter', type: 'options', description: 'Uses the exact explicit comparison transport advertised by LifeSpace. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.', typeOptions: { loadOptionsMethod: 'getComparisonOperatorsForCurrentField', loadOptionsDependsOn: ['spaceId', 'recordType', '&field'] }, options: [], default: '', required: true },
689
717
  { displayName: 'Value', name: 'value', type: 'dateTime', default: '', required: true },
690
718
  ] },
719
+ { displayName: 'Local Date Window', name: 'localDateWindow', values: [
720
+ { displayName: 'Field Name or ID', name: 'field', type: 'options', typeOptions: { loadOptionsMethod: 'getLocalDateWindowFields', loadOptionsDependsOn: ['spaceId', 'recordType'] }, options: [], default: '', required: true, description: 'Choose a datetime field whose published Time Semantics includes a local-date-window transport. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.' },
721
+ { displayName: 'Start Date', name: 'dateStart', type: 'dateTime', default: '', required: true, description: 'Inclusive local calendar start date. The adapter sends YYYY-MM-DD and does not calculate UTC boundaries.' },
722
+ { displayName: 'End Date (Exclusive)', name: 'dateEndExclusive', type: 'dateTime', default: '', required: true, description: 'Exclusive local calendar end date' },
723
+ { displayName: 'Viewing Timezone', name: 'timezone', type: 'string', default: '', required: true, placeholder: 'Europe/Amsterdam', description: 'IANA timezone passed unchanged to LifeSpace Core' },
724
+ ] },
691
725
  { displayName: 'Person Filter', name: 'person', values: [
692
726
  { displayName: 'Field Name or ID', name: 'field', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getPersonFilterableFields', loadOptionsDependsOn: ['spaceId', 'recordType'] }, options: [], default: '', required: true },
693
727
  { displayName: 'Person Name or ID', name: 'target', type: 'options', description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>', typeOptions: { loadOptionsMethod: 'getRelationTargetsForCurrentField', loadOptionsDependsOn: ['spaceId', 'recordType', '&field'] }, options: [], default: '', required: true },
@@ -700,43 +734,33 @@ class LifeSpace {
700
734
  ],
701
735
  },
702
736
  {
703
- displayName: 'Local Date Windows', name: 'localDateWindows', type: 'fixedCollection', default: {},
704
- placeholder: 'Add Local Date Window', typeOptions: { multipleValues: true },
705
- displayOptions: { show: { resource: ['modelRecord'], operation: ['list'] } },
706
- options: [{ displayName: 'Window', name: 'window', values: [
707
- { displayName: 'Field Name or ID', name: 'field', type: 'options', typeOptions: { loadOptionsMethod: 'getLocalDateWindowFields', loadOptionsDependsOn: ['spaceId', 'recordType'] }, options: [], default: '', required: true, description: 'Only datetime fields whose published comparison semantics include a local-date-window transport are offered. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.' },
708
- { displayName: 'Start Date', name: 'dateStart', type: 'dateTime', default: '', required: true, description: 'Inclusive local calendar start date. The adapter submits YYYY-MM-DD and does not calculate UTC boundaries.' },
709
- { displayName: 'End Date (Exclusive)', name: 'dateEndExclusive', type: 'dateTime', default: '', required: true, description: 'Exclusive local calendar end date' },
710
- { displayName: 'Viewing Timezone', name: 'timezone', type: 'string', default: '', required: true, placeholder: 'Europe/Amsterdam', description: 'IANA timezone passed unchanged to LifeSpace Core' },
711
- ] }],
712
- },
713
- {
714
- displayName: 'Semantic Query Name or ID', name: 'semanticQueryKey', type: 'options',
737
+ displayName: 'Capability Query Name or ID', name: 'semanticQueryKey', type: 'options', noDataExpression: true,
715
738
  typeOptions: { loadOptionsMethod: 'getCapabilityQueries', loadOptionsDependsOn: ['spaceId', 'recordType'] },
716
- options: [], default: '',
717
- displayOptions: { show: { resource: ['modelRecord'], operation: ['list'] } },
718
- description: 'Optional grouped capability query published by LifeSpace, for example Calendar Window. The adapter does not hard-code Event fields. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
739
+ options: [], default: '', required: true,
740
+ displayOptions: { show: { resource: ['modelRecord'], operation: ['list'], queryMode: ['capability'] } },
741
+ description: 'Choose a grouped capability query published by the selected LifeSpace Record Type. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
719
742
  },
720
743
  {
721
- displayName: 'Semantic Query Input', name: 'semanticQueryInput', type: 'resourceMapper',
744
+ displayName: 'Capability Query Parameters', name: 'semanticQueryInput', type: 'resourceMapper',
722
745
  default: { mappingMode: 'defineBelow', value: null }, noDataExpression: true,
723
746
  typeOptions: {
724
747
  loadOptionsDependsOn: ['spaceId', 'recordType', 'semanticQueryKey'],
725
748
  resourceMapper: {
726
749
  resourceMapperMethod: 'getSemanticQueryInputFields', mode: 'add',
727
- fieldWords: { singular: 'parameter', plural: 'parameters' }, addAllFields: true, supportAutoMap: false,
728
- noFieldsError: 'Choose a Semantic Query to load its LifeSpace-published parameters.',
750
+ fieldWords: { singular: 'query parameter', plural: 'query parameters' },
751
+ addAllFields: true, supportAutoMap: false,
752
+ noFieldsError: 'Choose a Capability Query to load its LifeSpace-published parameters.',
729
753
  },
730
754
  },
731
- displayOptions: { show: { resource: ['modelRecord'], operation: ['list'] } },
732
- description: 'Parameters are projected directly from query.capabilityQueries. Calendar local dates/timezone are sent to Core unchanged; Core owns overlap and DST semantics.',
755
+ displayOptions: { show: { resource: ['modelRecord'], operation: ['list'], queryMode: ['capability'] } },
756
+ description: 'Parameters are projected directly from query.capabilityQueries. Local dates/timezone are sent to Core unchanged; Core owns overlap and DST semantics.',
733
757
  },
734
758
  {
735
- displayName: 'Semantic Sort Name or ID', name: 'semanticSort', type: 'options',
759
+ displayName: 'Capability Sort Name or ID', name: 'semanticSort', type: 'options',
736
760
  typeOptions: { loadOptionsMethod: 'getSemanticSorts', loadOptionsDependsOn: ['spaceId', 'recordType', 'semanticQueryKey'] },
737
761
  options: [], default: '',
738
- displayOptions: { show: { resource: ['modelRecord'], operation: ['list'] } },
739
- description: 'Optional capability-specific sort published with the selected Semantic Query. Do not combine with field Sorts. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
762
+ displayOptions: { show: { resource: ['modelRecord'], operation: ['list'], queryMode: ['capability'] } },
763
+ description: 'Optional capability-specific ordering published with the selected Capability Query. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
740
764
  },
741
765
  {
742
766
  displayName: 'Return All', name: 'returnAll', type: 'boolean', default: false,
@@ -752,7 +776,7 @@ class LifeSpace {
752
776
  {
753
777
  displayName: 'Sorts', name: 'sorts', type: 'fixedCollection', default: {},
754
778
  placeholder: 'Add Sort', typeOptions: { multipleValues: true },
755
- displayOptions: { show: { resource: ['modelRecord'], operation: ['list'] } },
779
+ displayOptions: { show: { resource: ['modelRecord'], operation: ['list'], queryMode: ['standard'] } },
756
780
  options: [{ displayName: 'Sort', name: 'sort', values: [
757
781
  { displayName: 'Field Name or ID', name: 'field', type: 'options', typeOptions: { loadOptionsMethod: 'getSortableFields' }, options: [], default: '', required: true, description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>' },
758
782
  { displayName: 'Direction', name: 'direction', type: 'options', options: [{ name: 'Ascending', value: 'asc' }, { name: 'Descending', value: 'desc' }], default: 'asc' },
@@ -927,6 +951,27 @@ class LifeSpace {
927
951
  };
928
952
  });
929
953
  },
954
+ async getQueryModes() {
955
+ var _a;
956
+ const modes = [{
957
+ name: 'Standard Query',
958
+ value: 'standard',
959
+ description: 'Search, filters, comparisons, local-date windows, and generic sorting',
960
+ }];
961
+ try {
962
+ const selected = await optionModel(this);
963
+ if (((_a = selected === null || selected === void 0 ? void 0 : selected.model.query.capabilityQueries) !== null && _a !== void 0 ? _a : []).length > 0) {
964
+ modes.push({
965
+ name: 'Capability Query',
966
+ value: 'capability',
967
+ description: 'Use one grouped query contract published by a LifeSpace Capability',
968
+ });
969
+ }
970
+ }
971
+ catch {
972
+ }
973
+ return modes;
974
+ },
930
975
  async getCapabilityQueries() {
931
976
  var _a;
932
977
  const selected = await optionModel(this);