n8n-nodes-lifespace 0.1.22 → 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -44,25 +44,26 @@ Restart n8n afterwards. In queue mode, install the package anywhere that may exe
44
44
 
45
45
  ## Quick start
46
46
 
47
- Create a **LifeSpace API** credential for Runtime Discovery and LifeSpace API calls.
47
+ LifeSpace has two explicit execution modes in n8n.
48
48
 
49
- It contains:
49
+ For **Service Principal(服务主体)automation**, create a **LifeSpace API** credential with the Core **API Base URL** and an opaque `lsp_pat_*` **Service API Token**. This remains the default for scheduled/system workflows where `Principal = Actor = Service`.
50
50
 
51
- - **API Base URL**, for example:
51
+ For a user-initiated **Delegated Agent(委托智能体)** Tool or Batch, create a separate **LifeSpace Delegated Agent API** credential containing:
52
52
 
53
- ```text
54
- https://api.example.com/api/v1
55
- ```
53
+ - **Core API Base URL**;
54
+ - **Identity API Base URL**;
55
+ - the server-only Application credential `lsa_*`;
56
+ - the registered LifeSpace `agt_*` Agent ID.
56
57
 
57
- - **Service API Token**, an opaque LifeSpace `lsp_pat_*` token.
58
+ Delegated mode does **not** require or fall back to a Service PAT. The adapter uses the Application credential only to mint a short-lived `User Principal → Agent Actor` execution token from LifeSpace Identity, then calls Core with that token plus explicit Delegation selectors.
58
59
 
59
- The API Base URL is the LifeSpace Core API root. Do not include a Space ID or Record Type path.
60
+ `Principal User ID`, `Delegation ID`, `Read Delegation ID` and per-item Batch Delegation IDs are execution/control context, never LLM Tool arguments. `Read Delegation ID` is intentionally separate: it covers delegated design-time Discovery and only the runtime calls that actually need relation-name lookup or optimistic-concurrency pre-reads, so those helper reads cannot accidentally consume a single-use Delegation reserved for the actual query/mutation/action. A pure runtime mutation with no helper read does not exercise the Read Delegation.
60
61
 
61
- The token is shown only when it is created or rotated. Store it in n8n immediately; do not put it in workflow fields, URLs, source files, or workflow exports.
62
+ Credentials are secrets. Store them only in n8n credentials; do not put `lsp_pat_*`, `lsa_*`, Agent JWTs or other secrets in workflow fields, URLs, source files or workflow exports.
62
63
 
63
64
  A **LifeSpace Trigger** additionally uses a **LifeSpace Webhook Signing** credential containing the endpoint-scoped HMAC signing secret. This is deliberately separate from the API credential: the Service API Token authenticates outbound n8n → LifeSpace calls, while the signing secret verifies inbound LifeSpace → n8n deliveries and rotates with its Webhook Endpoint. The Trigger still reuses the LifeSpace API credential for Space/Record Type discovery, so API context is not duplicated.
64
65
 
65
- Runtime Discovery determines which Spaces, Record Types, fields, queries, Actions and relation lookup capabilities the current API credential can use. The adapter starts from the compact current-principal inventory, then loads static semantic detail only for the selected Space/Record Type. Relation target lookup remains lazy and field-scoped. Execution authorization is still enforced by LifeSpace from the current principal, credential scope, Application × Model Access and current Space/Data Grant authority.
66
+ Runtime Discovery determines which Spaces, Record Types, fields, queries, Actions and relation lookup capabilities the current execution context can use. Service mode uses the Service PAT. Delegated Agent mode uses the short-lived Agent token; progressive semantic detail uses the configured Read Delegation selector. Relation target lookup remains lazy and field-scoped. Execution authorization is always enforced by LifeSpace from verified Application / Principal / Actor, credential scope, Application × Model Access, current Space/Data Grant authority and the explicitly selected Delegation when Principal and Actor differ.
66
67
 
67
68
  ## Generated Record UX
68
69
 
@@ -89,7 +90,7 @@ Current Calendar models expose the canonical `capabilityBindings.calendar.rangeF
89
90
 
90
91
  ## LifeSpace contract compatibility
91
92
 
92
- This package follows the current LifeSpace Core Kernel `0.36.0` contract family. It consumes Runtime/Discovery semantics only; Eventing configuration and webhook delivery semantics are owned independently by Integration/Eventing `0.1.0`.
93
+ This package follows the current LifeSpace Core Kernel `0.40.0` contract family. Integration/Eventing is independently versioned at `0.3.0`. Authority v2 requires an explicit Delegation selector whenever `Principal != Actor`, and Model Contracts expose the first-class atomic Generic Runtime Batch surface.
93
94
 
94
95
  The UX depends on these Kernel capabilities:
95
96
 
@@ -112,6 +113,7 @@ The UX depends on these Kernel capabilities:
112
113
  - `0.34.0`: explicit `eq/lt/lte/gt/gte` comparison transports, first-class `createdAt` / `updatedAt` envelope comparisons and Core-owned datetime local-date-window conversion become discoverable.
113
114
  - `0.35.0`: grouped `query.capabilityQueries` introduced capability-owned compatibility queries.
114
115
  - `0.36.0`: `query.canonical` unifies Search, typed Boolean Filter, multi-Sort and cursor Pagination behind `POST .../records/query`; Runtime Discovery also exposes canonical `instant`, `range<date>`, `range<instant>`, `temporal_range` field types and the Calendar `rangeField` binding. The human Workflow node consumes those current semantics while retaining historical Discovery spellings only for compatibility.
116
+ - `0.40.0`: Authority v2 separates verified Application / Principal / Actor from the explicit opaque Delegation selector; one Delegation has one Space/Model/Record scope; first-class `POST /spaces/{spaceId}/models/batch` atomically commits 1-20 create/update/delete operations and returns one `changeSetId`.
115
117
 
116
118
  The adapter prefers the `0.27+` progressive flow while configuring a node:
117
119
 
@@ -181,7 +183,11 @@ Credentials are intentionally static secure configuration and are not workflow-e
181
183
 
182
184
  The normal n8n-facing resource is **Record**. LifeSpace still owns **Model** semantics internally; the adapter uses **Record Type** for the workflow-facing selection.
183
185
 
184
- Supported operations:
186
+ Supported human Workflow operations include ordinary Record CRUD/Query/Action plus **Batch Mutation**. Batch Mutation accepts 1-20 create/update/delete operations, may mix Record Types in one Space, and sends exactly one atomic Core request. Update/Delete items carry their already-known optimistic-concurrency `version`; the adapter does not turn Batch into N pre-read + CRUD calls.
187
+
188
+ The native Agent Tool supports Query/Create/Update/Delete/Action plus **Create Records (Batch)** for 1-20 records of its pinned Record Type. Agent Batch Update/Delete are intentionally not synthesized in this version: automatically resolving N current versions would reintroduce an N-read waterfall. Use the human Batch surface with known versions, or add a future set-based version-resolution contract before exposing those Agent operations.
189
+
190
+ Ordinary Record operations remain:
185
191
 
186
192
  - **Create**;
187
193
  - **Get**;
@@ -333,13 +339,16 @@ Webhook Endpoint / Event Subscription creation is intentionally not performed by
333
339
 
334
340
  ## What the package provides
335
341
 
336
- - **LifeSpace API** credential for API authentication and Runtime Discovery;
342
+ - **LifeSpace API** credential for Service Principal authentication and Runtime Discovery;
343
+ - **LifeSpace Delegated Agent API** credential for server-only Application authentication, short-lived Agent token minting, and delegated Core execution without a Service PAT;
337
344
  - **LifeSpace Webhook Signing** credential for endpoint-scoped inbound HMAC verification;
338
- - **LifeSpace** human workflow node with Discovery-driven Record operations plus advanced API Request;
339
- - **LifeSpace Agent Tool** native AiTool with Discovery-driven semantic schemas;
345
+ - **LifeSpace** human workflow node with Discovery-driven Record operations, first-class 1-20 atomic Batch create/update/delete, plus advanced API Request;
346
+ - **LifeSpace Agent Tool** native AiTool with Discovery-driven semantic schemas and bounded same-model Batch Create;
340
347
  - **LifeSpace Trigger** with signed multi-Record-Type Domain Event filtering;
341
348
  - shared thin adapter projection from LifeSpace Runtime Discovery into human n8n controls and model-facing Tool schemas.
342
349
 
350
+ The human Batch surface is the generic cross-model atomic transport. The Agent Tool deliberately exposes only same-model Batch Create in this release: Agent Update/Delete continue to resolve optimistic concurrency through a read before mutation, and the adapter does not turn multiple such reads into an N+1 pseudo-Batch. Cross-model or mutation-mixed Agent Batch should be added only when its full input contract can remain deterministic and bounded without reintroducing per-item Runtime round trips.
351
+
343
352
  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.
344
353
 
345
354
  ## Architecture boundary
@@ -0,0 +1,13 @@
1
+ import type { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
2
+ export declare class LifeSpaceAgentExecutionApi implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ icon: {
6
+ readonly light: "file:lifespace.svg";
7
+ readonly dark: "file:lifespace.dark.svg";
8
+ };
9
+ documentationUrl: string;
10
+ properties: INodeProperties[];
11
+ authenticate: IAuthenticateGeneric;
12
+ test: ICredentialTestRequest;
13
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LifeSpaceAgentExecutionApi = void 0;
4
+ class LifeSpaceAgentExecutionApi {
5
+ constructor() {
6
+ this.name = 'lifeSpaceAgentExecutionApi';
7
+ this.displayName = 'LifeSpace Delegated Agent API';
8
+ this.icon = {
9
+ light: 'file:lifespace.svg',
10
+ dark: 'file:lifespace.dark.svg',
11
+ };
12
+ this.documentationUrl = 'https://github.com/huangshirui/LifeSpace/blob/main/docs/application-authentication.md';
13
+ this.properties = [
14
+ {
15
+ displayName: 'Core API Base URL',
16
+ name: 'coreBaseUrl',
17
+ type: 'string',
18
+ default: '',
19
+ placeholder: 'https://core.example.com/api/v1',
20
+ required: true,
21
+ description: 'LifeSpace Core API root used by delegated Agent business requests',
22
+ },
23
+ {
24
+ displayName: 'Identity API Base URL',
25
+ name: 'identityBaseUrl',
26
+ type: 'string',
27
+ default: '',
28
+ placeholder: 'https://identity.example.com',
29
+ required: true,
30
+ description: 'LifeSpace Identity origin. Do not include /internal/v1/agent-tokens',
31
+ },
32
+ {
33
+ displayName: 'Application Credential',
34
+ name: 'applicationSecret',
35
+ type: 'string',
36
+ typeOptions: { password: true },
37
+ default: '',
38
+ placeholder: 'lsa_...',
39
+ required: true,
40
+ description: 'Server-only trusted LifeSpace Application credential used only to mint short-lived delegated Agent execution tokens',
41
+ },
42
+ {
43
+ displayName: 'Agent ID',
44
+ name: 'agentId',
45
+ type: 'string',
46
+ default: '',
47
+ placeholder: 'agt_...',
48
+ required: true,
49
+ description: 'LifeSpace Agent identity bound to this registered Application',
50
+ },
51
+ ];
52
+ this.authenticate = {
53
+ type: 'generic',
54
+ properties: {
55
+ headers: {
56
+ Authorization: '=Bearer {{$credentials.applicationSecret}}',
57
+ },
58
+ },
59
+ };
60
+ this.test = {
61
+ request: {
62
+ baseURL: '={{$credentials.identityBaseUrl.endsWith("/") ? $credentials.identityBaseUrl.slice(0, -1) : $credentials.identityBaseUrl}}',
63
+ url: '/internal/v1/agents',
64
+ method: 'GET',
65
+ },
66
+ };
67
+ }
68
+ }
69
+ exports.LifeSpaceAgentExecutionApi = LifeSpaceAgentExecutionApi;
70
+ //# sourceMappingURL=LifeSpaceAgentExecutionApi.credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LifeSpaceAgentExecutionApi.credentials.js","sourceRoot":"","sources":["../../credentials/LifeSpaceAgentExecutionApi.credentials.ts"],"names":[],"mappings":";;;AAOA,MAAa,0BAA0B;IAAvC;QACE,SAAI,GAAG,4BAA4B,CAAC;QAEpC,gBAAW,GAAG,+BAA+B,CAAC;QAE9C,SAAI,GAAG;YACL,KAAK,EAAE,oBAAoB;YAC3B,IAAI,EAAE,yBAAyB;SACvB,CAAC;QAEX,qBAAgB,GAAG,uFAAuF,CAAC;QAE3G,eAAU,GAAsB;YAC9B;gBACE,WAAW,EAAE,mBAAmB;gBAChC,IAAI,EAAE,aAAa;gBACnB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,iCAAiC;gBAC9C,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,mEAAmE;aACjF;YACD;gBACE,WAAW,EAAE,uBAAuB;gBACpC,IAAI,EAAE,iBAAiB;gBACvB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,8BAA8B;gBAC3C,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,qEAAqE;aACnF;YACD;gBACE,WAAW,EAAE,wBAAwB;gBACrC,IAAI,EAAE,mBAAmB;gBACzB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,SAAS;gBACtB,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,qHAAqH;aACnI;YACD;gBACE,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,SAAS;gBACtB,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,+DAA+D;aAC7E;SACF,CAAC;QAEF,iBAAY,GAAyB;YACnC,IAAI,EAAE,SAAS;YACf,UAAU,EAAE;gBACV,OAAO,EAAE;oBACP,aAAa,EAAE,4CAA4C;iBAC5D;aACF;SACF,CAAC;QAEF,SAAI,GAA2B;YAC7B,OAAO,EAAE;gBACP,OAAO,EAAE,4HAA4H;gBACrI,GAAG,EAAE,qBAAqB;gBAC1B,MAAM,EAAE,KAAK;aACd;SACF,CAAC;IACJ,CAAC;CAAA;AApED,gEAoEC"}
@@ -4,6 +4,7 @@ exports.LifeSpace = void 0;
4
4
  const n8n_workflow_1 = require("n8n-workflow");
5
5
  const lifespaceDiscovery_1 = require("../lifespaceDiscovery");
6
6
  const lifeSpaceActionSnapshot_1 = require("../shared/lifeSpaceActionSnapshot");
7
+ const lifeSpaceExecutionAuthority_1 = require("../shared/lifeSpaceExecutionAuthority");
7
8
  const LEGACY_MODEL_ROUTE_TO_KEY = Object.freeze({
8
9
  tasks: 'task',
9
10
  wishes: 'wish',
@@ -86,6 +87,81 @@ function parseJsonObject(context, itemIndex, value, fieldName) {
86
87
  }
87
88
  return parsed;
88
89
  }
90
+ function batchMutationOperations(context, itemIndex, value, delegated) {
91
+ let parsed;
92
+ try {
93
+ parsed = typeof value === 'string' ? JSON.parse(value) : value;
94
+ }
95
+ catch (error) {
96
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), error, { itemIndex });
97
+ }
98
+ if (!Array.isArray(parsed) || parsed.length < 1 || parsed.length > 20) {
99
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), 'Batch Operations must be a JSON array containing 1 to 20 create/update/delete operations', { itemIndex });
100
+ }
101
+ return parsed.map((raw, index) => {
102
+ var _a, _b, _c, _d;
103
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
104
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Batch operation ${index} must be a JSON object`, { itemIndex });
105
+ }
106
+ const input = raw;
107
+ const operation = String((_a = input.operation) !== null && _a !== void 0 ? _a : '');
108
+ const modelKey = String((_b = input.modelKey) !== null && _b !== void 0 ? _b : '').trim();
109
+ if (!['create', 'update', 'delete'].includes(operation)) {
110
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Batch operation ${index} must be create, update, or delete`, { itemIndex });
111
+ }
112
+ if (!/^[A-Za-z][A-Za-z0-9_]{0,99}$/u.test(modelKey)) {
113
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Batch operation ${index} has an invalid modelKey`, { itemIndex });
114
+ }
115
+ const delegationId = String((_c = input.delegationId) !== null && _c !== void 0 ? _c : '').trim();
116
+ if (delegated && !/^dlg_[A-Za-z0-9_-]+$/u.test(delegationId)) {
117
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Batch operation ${index} requires its own valid delegationId in Delegated Agent mode`, { itemIndex });
118
+ }
119
+ if (!delegated && delegationId) {
120
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Batch operation ${index} cannot use delegationId in Service Principal mode`, { itemIndex });
121
+ }
122
+ if (operation === 'create') {
123
+ const data = input.data;
124
+ if (!data || typeof data !== 'object' || Array.isArray(data)) {
125
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Batch create ${index} requires an object data field`, { itemIndex });
126
+ }
127
+ return {
128
+ operation: 'create',
129
+ modelKey,
130
+ data: data,
131
+ ...(delegationId ? { delegationId } : {}),
132
+ };
133
+ }
134
+ const recordId = String((_d = input.recordId) !== null && _d !== void 0 ? _d : '').trim();
135
+ const version = Number(input.version);
136
+ if (!/^rec_[A-Za-z0-9_-]+$/u.test(recordId)) {
137
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Batch operation ${index} has an invalid recordId`, { itemIndex });
138
+ }
139
+ if (!Number.isInteger(version) || version < 1) {
140
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Batch operation ${index} requires a positive integer version`, { itemIndex });
141
+ }
142
+ if (operation === 'delete') {
143
+ return {
144
+ operation: 'delete',
145
+ modelKey,
146
+ recordId,
147
+ version,
148
+ ...(delegationId ? { delegationId } : {}),
149
+ };
150
+ }
151
+ const data = input.data;
152
+ if (!data || typeof data !== 'object' || Array.isArray(data) || Object.keys(data).length === 0) {
153
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `Batch update ${index} requires non-empty object data`, { itemIndex });
154
+ }
155
+ return {
156
+ operation: 'update',
157
+ modelKey,
158
+ recordId,
159
+ version,
160
+ data: data,
161
+ ...(delegationId ? { delegationId } : {}),
162
+ };
163
+ });
164
+ }
89
165
  function mappedValue(context, itemIndex, parameterName) {
90
166
  const value = context.getNodeParameter(`${parameterName}.value`, itemIndex, {});
91
167
  return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
@@ -565,7 +641,7 @@ class LifeSpace {
565
641
  },
566
642
  group: ['transform'],
567
643
  version: 1,
568
- subtitle: '={{$parameter["resource"] === "modelRecord" ? $parameter["operation"] : "API Request"}}',
644
+ subtitle: '={{$parameter["resource"] === "modelRecord" ? $parameter["operation"] : ($parameter["resource"] === "batchMutation" ? "Batch Mutation" : "API Request")}}',
569
645
  description: 'Use LifeSpace records and APIs in n8n workflows',
570
646
  defaults: {
571
647
  name: 'LifeSpace',
@@ -577,6 +653,22 @@ class LifeSpace {
577
653
  {
578
654
  name: 'lifeSpaceApi',
579
655
  required: true,
656
+ displayOptions: {
657
+ hide: {
658
+ resource: ['batchMutation'],
659
+ batchAuthorityMode: ['delegatedAgent'],
660
+ },
661
+ },
662
+ },
663
+ {
664
+ name: 'lifeSpaceAgentExecutionApi',
665
+ required: true,
666
+ displayOptions: {
667
+ show: {
668
+ resource: ['batchMutation'],
669
+ batchAuthorityMode: ['delegatedAgent'],
670
+ },
671
+ },
580
672
  },
581
673
  ],
582
674
  properties: [
@@ -590,6 +682,10 @@ class LifeSpace {
590
682
  name: 'Record',
591
683
  value: 'modelRecord',
592
684
  },
685
+ {
686
+ name: 'Batch Mutation',
687
+ value: 'batchMutation',
688
+ },
593
689
  {
594
690
  name: 'API Request',
595
691
  value: 'apiRequest',
@@ -597,6 +693,50 @@ class LifeSpace {
597
693
  ],
598
694
  default: 'modelRecord',
599
695
  },
696
+ {
697
+ displayName: 'Batch Authority Mode',
698
+ name: 'batchAuthorityMode',
699
+ type: 'options',
700
+ noDataExpression: true,
701
+ displayOptions: { show: { resource: ['batchMutation'] } },
702
+ options: [
703
+ {
704
+ name: 'Service Principal',
705
+ value: 'service',
706
+ description: 'Use the existing LifeSpace Service API Token for the atomic Batch',
707
+ },
708
+ {
709
+ name: 'Delegated Agent',
710
+ value: 'delegatedAgent',
711
+ description: 'Mint one short-lived User Principal -> Agent Actor token; every Batch item supplies its explicit Delegation selector',
712
+ },
713
+ ],
714
+ default: 'service',
715
+ },
716
+ {
717
+ displayName: 'Principal User ID',
718
+ name: 'batchPrincipalUserId',
719
+ type: 'string',
720
+ default: '',
721
+ required: true,
722
+ placeholder: 'usr_...',
723
+ displayOptions: {
724
+ show: {
725
+ resource: ['batchMutation'],
726
+ batchAuthorityMode: ['delegatedAgent'],
727
+ },
728
+ },
729
+ description: 'Execution context for delegated Batch only. This value is not part of any AI Tool input.',
730
+ },
731
+ {
732
+ displayName: 'Batch Operations',
733
+ name: 'batchOperations',
734
+ type: 'json',
735
+ default: '[]',
736
+ required: true,
737
+ displayOptions: { show: { resource: ['batchMutation'] } },
738
+ description: 'JSON array with 1-20 create/update/delete operations. Update/delete require recordId and version. Delegated Agent mode requires delegationId on every item. The adapter sends one atomic Core Batch request and performs no rollback logic itself.',
739
+ },
600
740
  {
601
741
  displayName: 'Operation',
602
742
  name: 'operation',
@@ -616,7 +756,7 @@ class LifeSpace {
616
756
  {
617
757
  displayName: 'Space Name or ID', name: 'spaceId', type: 'options',
618
758
  typeOptions: { loadOptionsMethod: 'getSpaces' }, options: [], default: '', required: true,
619
- displayOptions: { show: { resource: ['modelRecord'] } },
759
+ displayOptions: { show: { resource: ['modelRecord', 'batchMutation'] } },
620
760
  description: 'Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>',
621
761
  },
622
762
  {
@@ -1087,21 +1227,43 @@ class LifeSpace {
1087
1227
  };
1088
1228
  }
1089
1229
  async execute() {
1090
- var _a, _b, _c;
1230
+ var _a, _b, _c, _d;
1091
1231
  const items = this.getInputData();
1092
1232
  const output = [];
1093
- const credentials = await this.getCredentials('lifeSpaceApi');
1094
- const baseUrl = (0, lifespaceDiscovery_1.normalizeBaseUrl)(credentials.baseUrl);
1095
1233
  for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
1096
1234
  try {
1097
1235
  const resource = this.getNodeParameter('resource', itemIndex);
1098
1236
  let response;
1099
- if (resource === 'modelRecord') {
1237
+ if (resource === 'batchMutation') {
1238
+ const rawSpaceId = String(this.getNodeParameter('spaceId', itemIndex)).trim();
1239
+ if (!rawSpaceId) {
1240
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'LifeSpace Batch Mutation requires a Space ID', { itemIndex });
1241
+ }
1242
+ const batchAuthorityMode = String((_a = this.getNodeParameter('batchAuthorityMode', itemIndex, 'service')) !== null && _a !== void 0 ? _a : 'service');
1243
+ const baseUrl = batchAuthorityMode === 'delegatedAgent'
1244
+ ? (0, lifeSpaceExecutionAuthority_1.delegatedAgentCoreBaseUrl)(this, await this.getCredentials('lifeSpaceAgentExecutionApi', itemIndex))
1245
+ : (0, lifespaceDiscovery_1.normalizeBaseUrl)((await this.getCredentials('lifeSpaceApi', itemIndex)).baseUrl);
1246
+ const operations = batchMutationOperations(this, itemIndex, this.getNodeParameter('batchOperations', itemIndex, '[]'), batchAuthorityMode === 'delegatedAgent');
1247
+ const authority = await (0, lifeSpaceExecutionAuthority_1.executionAuthority)(this, itemIndex, 'write', {
1248
+ modeParameter: 'batchAuthorityMode',
1249
+ principalParameter: 'batchPrincipalUserId',
1250
+ delegationParameter: 'batchDelegationId',
1251
+ requireDelegation: false,
1252
+ });
1253
+ response = await (0, lifeSpaceExecutionAuthority_1.lifeSpaceRequest)(this, authority, {
1254
+ method: 'POST',
1255
+ url: `${baseUrl}/spaces/${encodeURIComponent(rawSpaceId)}/models/batch`,
1256
+ body: { operations },
1257
+ json: true,
1258
+ });
1259
+ }
1260
+ else if (resource === 'modelRecord') {
1261
+ const baseUrl = (0, lifespaceDiscovery_1.normalizeBaseUrl)((await this.getCredentials('lifeSpaceApi', itemIndex)).baseUrl);
1100
1262
  const operation = this.getNodeParameter('operation', itemIndex);
1101
1263
  const rawSpaceId = String(this.getNodeParameter('spaceId', itemIndex));
1102
1264
  const recordType = (0, lifespaceDiscovery_1.decodeRecordTypeSelector)(this.getNodeParameter('recordType', itemIndex, ''));
1103
- const legacyModelRoute = String((_a = this.getNodeParameter('modelRoute', itemIndex, '')) !== null && _a !== void 0 ? _a : '').trim();
1104
- const rawModelKey = (_b = recordType === null || recordType === void 0 ? void 0 : recordType.modelKey) !== null && _b !== void 0 ? _b : LEGACY_MODEL_ROUTE_TO_KEY[legacyModelRoute];
1265
+ const legacyModelRoute = String((_b = this.getNodeParameter('modelRoute', itemIndex, '')) !== null && _b !== void 0 ? _b : '').trim();
1266
+ const rawModelKey = (_c = recordType === null || recordType === void 0 ? void 0 : recordType.modelKey) !== null && _c !== void 0 ? _c : LEGACY_MODEL_ROUTE_TO_KEY[legacyModelRoute];
1105
1267
  if (!rawModelKey) {
1106
1268
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'LifeSpace Record Type selector is invalid. Choose a Record Type from Discovery or pass a Trigger recordType value.', { itemIndex });
1107
1269
  }
@@ -1110,7 +1272,7 @@ class LifeSpace {
1110
1272
  const collectionPath = `/spaces/${spaceId}/models/${modelKey}/records`;
1111
1273
  if (operation === 'list') {
1112
1274
  const returnAll = this.getNodeParameter('returnAll', itemIndex, false);
1113
- const canonical = String((_c = this.getNodeParameter('queryMode', itemIndex, '')) !== null && _c !== void 0 ? _c : '') === 'canonical';
1275
+ const canonical = String((_d = this.getNodeParameter('queryMode', itemIndex, '')) !== null && _d !== void 0 ? _d : '') === 'canonical';
1114
1276
  const queryPageRequest = (limit, cursor) => {
1115
1277
  if (canonical) {
1116
1278
  return {
@@ -1203,6 +1365,7 @@ class LifeSpace {
1203
1365
  }
1204
1366
  }
1205
1367
  else {
1368
+ const baseUrl = (0, lifespaceDiscovery_1.normalizeBaseUrl)((await this.getCredentials('lifeSpaceApi', itemIndex)).baseUrl);
1206
1369
  const method = this.getNodeParameter('method', itemIndex);
1207
1370
  const path = String(this.getNodeParameter('path', itemIndex));
1208
1371
  const options = {