@sensigo/realm 0.1.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 (222) hide show
  1. package/README.md +150 -0
  2. package/dist/adapters/adapter-utils.d.ts +14 -0
  3. package/dist/adapters/adapter-utils.d.ts.map +1 -0
  4. package/dist/adapters/adapter-utils.js +18 -0
  5. package/dist/adapters/adapter-utils.js.map +1 -0
  6. package/dist/adapters/airtable-adapter.d.ts +51 -0
  7. package/dist/adapters/airtable-adapter.d.ts.map +1 -0
  8. package/dist/adapters/airtable-adapter.js +425 -0
  9. package/dist/adapters/airtable-adapter.js.map +1 -0
  10. package/dist/adapters/file-adapter.d.ts +14 -0
  11. package/dist/adapters/file-adapter.d.ts.map +1 -0
  12. package/dist/adapters/file-adapter.js +89 -0
  13. package/dist/adapters/file-adapter.js.map +1 -0
  14. package/dist/adapters/github-adapter.d.ts +40 -0
  15. package/dist/adapters/github-adapter.d.ts.map +1 -0
  16. package/dist/adapters/github-adapter.js +286 -0
  17. package/dist/adapters/github-adapter.js.map +1 -0
  18. package/dist/adapters/gorgias-adapter.d.ts +40 -0
  19. package/dist/adapters/gorgias-adapter.d.ts.map +1 -0
  20. package/dist/adapters/gorgias-adapter.js +300 -0
  21. package/dist/adapters/gorgias-adapter.js.map +1 -0
  22. package/dist/adapters/http-adapter.d.ts +28 -0
  23. package/dist/adapters/http-adapter.d.ts.map +1 -0
  24. package/dist/adapters/http-adapter.js +97 -0
  25. package/dist/adapters/http-adapter.js.map +1 -0
  26. package/dist/adapters/mock-adapter.d.ts +16 -0
  27. package/dist/adapters/mock-adapter.d.ts.map +1 -0
  28. package/dist/adapters/mock-adapter.js +61 -0
  29. package/dist/adapters/mock-adapter.js.map +1 -0
  30. package/dist/adapters/notion-adapter.d.ts +54 -0
  31. package/dist/adapters/notion-adapter.d.ts.map +1 -0
  32. package/dist/adapters/notion-adapter.js +751 -0
  33. package/dist/adapters/notion-adapter.js.map +1 -0
  34. package/dist/adapters/parcelpanel-adapter.d.ts +60 -0
  35. package/dist/adapters/parcelpanel-adapter.d.ts.map +1 -0
  36. package/dist/adapters/parcelpanel-adapter.js +251 -0
  37. package/dist/adapters/parcelpanel-adapter.js.map +1 -0
  38. package/dist/adapters/rate-limiter.d.ts +26 -0
  39. package/dist/adapters/rate-limiter.d.ts.map +1 -0
  40. package/dist/adapters/rate-limiter.js +3 -0
  41. package/dist/adapters/rate-limiter.js.map +1 -0
  42. package/dist/adapters/shopify-adapter.d.ts +92 -0
  43. package/dist/adapters/shopify-adapter.d.ts.map +1 -0
  44. package/dist/adapters/shopify-adapter.js +415 -0
  45. package/dist/adapters/shopify-adapter.js.map +1 -0
  46. package/dist/adapters/slack-adapter.d.ts +18 -0
  47. package/dist/adapters/slack-adapter.d.ts.map +1 -0
  48. package/dist/adapters/slack-adapter.js +81 -0
  49. package/dist/adapters/slack-adapter.js.map +1 -0
  50. package/dist/adapters/token-bucket.d.ts +27 -0
  51. package/dist/adapters/token-bucket.d.ts.map +1 -0
  52. package/dist/adapters/token-bucket.js +109 -0
  53. package/dist/adapters/token-bucket.js.map +1 -0
  54. package/dist/config/secrets.d.ts +15 -0
  55. package/dist/config/secrets.d.ts.map +1 -0
  56. package/dist/config/secrets.js +33 -0
  57. package/dist/config/secrets.js.map +1 -0
  58. package/dist/engine/eligibility.d.ts +50 -0
  59. package/dist/engine/eligibility.d.ts.map +1 -0
  60. package/dist/engine/eligibility.js +267 -0
  61. package/dist/engine/eligibility.js.map +1 -0
  62. package/dist/engine/error-resolution.d.ts +20 -0
  63. package/dist/engine/error-resolution.d.ts.map +1 -0
  64. package/dist/engine/error-resolution.js +32 -0
  65. package/dist/engine/error-resolution.js.map +1 -0
  66. package/dist/engine/execution-loop.d.ts +101 -0
  67. package/dist/engine/execution-loop.d.ts.map +1 -0
  68. package/dist/engine/execution-loop.js +1156 -0
  69. package/dist/engine/execution-loop.js.map +1 -0
  70. package/dist/engine/lifecycle.d.ts +14 -0
  71. package/dist/engine/lifecycle.d.ts.map +1 -0
  72. package/dist/engine/lifecycle.js +17 -0
  73. package/dist/engine/lifecycle.js.map +1 -0
  74. package/dist/engine/precondition.d.ts +30 -0
  75. package/dist/engine/precondition.d.ts.map +1 -0
  76. package/dist/engine/precondition.js +125 -0
  77. package/dist/engine/precondition.js.map +1 -0
  78. package/dist/engine/prompt-template.d.ts +25 -0
  79. package/dist/engine/prompt-template.d.ts.map +1 -0
  80. package/dist/engine/prompt-template.js +66 -0
  81. package/dist/engine/prompt-template.js.map +1 -0
  82. package/dist/engine/render-template.d.ts +52 -0
  83. package/dist/engine/render-template.d.ts.map +1 -0
  84. package/dist/engine/render-template.js +548 -0
  85. package/dist/engine/render-template.js.map +1 -0
  86. package/dist/engine/state-guard.d.ts +15 -0
  87. package/dist/engine/state-guard.d.ts.map +1 -0
  88. package/dist/engine/state-guard.js +40 -0
  89. package/dist/engine/state-guard.js.map +1 -0
  90. package/dist/engine/trace-normalizer.d.ts +36 -0
  91. package/dist/engine/trace-normalizer.d.ts.map +1 -0
  92. package/dist/engine/trace-normalizer.js +146 -0
  93. package/dist/engine/trace-normalizer.js.map +1 -0
  94. package/dist/engine/trace-policy.d.ts +53 -0
  95. package/dist/engine/trace-policy.d.ts.map +1 -0
  96. package/dist/engine/trace-policy.js +35 -0
  97. package/dist/engine/trace-policy.js.map +1 -0
  98. package/dist/engine/workflow-context-loader.d.ts +9 -0
  99. package/dist/engine/workflow-context-loader.d.ts.map +1 -0
  100. package/dist/engine/workflow-context-loader.js +41 -0
  101. package/dist/engine/workflow-context-loader.js.map +1 -0
  102. package/dist/evidence/snapshot.d.ts +38 -0
  103. package/dist/evidence/snapshot.d.ts.map +1 -0
  104. package/dist/evidence/snapshot.js +53 -0
  105. package/dist/evidence/snapshot.js.map +1 -0
  106. package/dist/extensions/default-registry.d.ts +19 -0
  107. package/dist/extensions/default-registry.d.ts.map +1 -0
  108. package/dist/extensions/default-registry.js +31 -0
  109. package/dist/extensions/default-registry.js.map +1 -0
  110. package/dist/extensions/processor.d.ts +13 -0
  111. package/dist/extensions/processor.d.ts.map +1 -0
  112. package/dist/extensions/processor.js +3 -0
  113. package/dist/extensions/processor.js.map +1 -0
  114. package/dist/extensions/registry.d.ts +25 -0
  115. package/dist/extensions/registry.d.ts.map +1 -0
  116. package/dist/extensions/registry.js +43 -0
  117. package/dist/extensions/registry.js.map +1 -0
  118. package/dist/extensions/service-adapter.d.ts +35 -0
  119. package/dist/extensions/service-adapter.d.ts.map +1 -0
  120. package/dist/extensions/service-adapter.js +3 -0
  121. package/dist/extensions/service-adapter.js.map +1 -0
  122. package/dist/extensions/step-handler.d.ts +28 -0
  123. package/dist/extensions/step-handler.d.ts.map +1 -0
  124. package/dist/extensions/step-handler.js +3 -0
  125. package/dist/extensions/step-handler.js.map +1 -0
  126. package/dist/handlers/primitives/compare-strings.d.ts +13 -0
  127. package/dist/handlers/primitives/compare-strings.d.ts.map +1 -0
  128. package/dist/handlers/primitives/compare-strings.js +28 -0
  129. package/dist/handlers/primitives/compare-strings.js.map +1 -0
  130. package/dist/handlers/primitives/count-results.d.ts +21 -0
  131. package/dist/handlers/primitives/count-results.d.ts.map +1 -0
  132. package/dist/handlers/primitives/count-results.js +23 -0
  133. package/dist/handlers/primitives/count-results.js.map +1 -0
  134. package/dist/handlers/primitives/partition-by-substring.d.ts +18 -0
  135. package/dist/handlers/primitives/partition-by-substring.d.ts.map +1 -0
  136. package/dist/handlers/primitives/partition-by-substring.js +28 -0
  137. package/dist/handlers/primitives/partition-by-substring.js.map +1 -0
  138. package/dist/handlers/primitives/resolve-resource.d.ts +13 -0
  139. package/dist/handlers/primitives/resolve-resource.d.ts.map +1 -0
  140. package/dist/handlers/primitives/resolve-resource.js +21 -0
  141. package/dist/handlers/primitives/resolve-resource.js.map +1 -0
  142. package/dist/handlers/primitives/walk-field.d.ts +11 -0
  143. package/dist/handlers/primitives/walk-field.d.ts.map +1 -0
  144. package/dist/handlers/primitives/walk-field.js +31 -0
  145. package/dist/handlers/primitives/walk-field.js.map +1 -0
  146. package/dist/handlers/validate-field-match.d.ts +11 -0
  147. package/dist/handlers/validate-field-match.d.ts.map +1 -0
  148. package/dist/handlers/validate-field-match.js +39 -0
  149. package/dist/handlers/validate-field-match.js.map +1 -0
  150. package/dist/handlers/validate-verbatim-quotes.d.ts +11 -0
  151. package/dist/handlers/validate-verbatim-quotes.d.ts.map +1 -0
  152. package/dist/handlers/validate-verbatim-quotes.js +37 -0
  153. package/dist/handlers/validate-verbatim-quotes.js.map +1 -0
  154. package/dist/index.d.ts +57 -0
  155. package/dist/index.d.ts.map +1 -0
  156. package/dist/index.js +45 -0
  157. package/dist/index.js.map +1 -0
  158. package/dist/pipeline/processing-pipeline.d.ts +24 -0
  159. package/dist/pipeline/processing-pipeline.d.ts.map +1 -0
  160. package/dist/pipeline/processing-pipeline.js +53 -0
  161. package/dist/pipeline/processing-pipeline.js.map +1 -0
  162. package/dist/processors/compute-hash.d.ts +4 -0
  163. package/dist/processors/compute-hash.d.ts.map +1 -0
  164. package/dist/processors/compute-hash.js +14 -0
  165. package/dist/processors/compute-hash.js.map +1 -0
  166. package/dist/processors/normalize-text.d.ts +8 -0
  167. package/dist/processors/normalize-text.d.ts.map +1 -0
  168. package/dist/processors/normalize-text.js +47 -0
  169. package/dist/processors/normalize-text.js.map +1 -0
  170. package/dist/store/json-file-store.d.ts +24 -0
  171. package/dist/store/json-file-store.d.ts.map +1 -0
  172. package/dist/store/json-file-store.js +210 -0
  173. package/dist/store/json-file-store.js.map +1 -0
  174. package/dist/store/store-interface.d.ts +33 -0
  175. package/dist/store/store-interface.d.ts.map +1 -0
  176. package/dist/store/store-interface.js +2 -0
  177. package/dist/store/store-interface.js.map +1 -0
  178. package/dist/store/trace-buffer-store.d.ts +55 -0
  179. package/dist/store/trace-buffer-store.d.ts.map +1 -0
  180. package/dist/store/trace-buffer-store.js +113 -0
  181. package/dist/store/trace-buffer-store.js.map +1 -0
  182. package/dist/types/mcp-types.d.ts +17 -0
  183. package/dist/types/mcp-types.d.ts.map +1 -0
  184. package/dist/types/mcp-types.js +5 -0
  185. package/dist/types/mcp-types.js.map +1 -0
  186. package/dist/types/response-envelope.d.ts +96 -0
  187. package/dist/types/response-envelope.d.ts.map +1 -0
  188. package/dist/types/response-envelope.js +2 -0
  189. package/dist/types/response-envelope.js.map +1 -0
  190. package/dist/types/run-record.d.ts +169 -0
  191. package/dist/types/run-record.d.ts.map +1 -0
  192. package/dist/types/run-record.js +2 -0
  193. package/dist/types/run-record.js.map +1 -0
  194. package/dist/types/workflow-definition.d.ts +292 -0
  195. package/dist/types/workflow-definition.d.ts.map +1 -0
  196. package/dist/types/workflow-definition.js +2 -0
  197. package/dist/types/workflow-definition.js.map +1 -0
  198. package/dist/types/workflow-error.d.ts +26 -0
  199. package/dist/types/workflow-error.d.ts.map +1 -0
  200. package/dist/types/workflow-error.js +28 -0
  201. package/dist/types/workflow-error.js.map +1 -0
  202. package/dist/utils/schema-skeleton.d.ts +11 -0
  203. package/dist/utils/schema-skeleton.d.ts.map +1 -0
  204. package/dist/utils/schema-skeleton.js +40 -0
  205. package/dist/utils/schema-skeleton.js.map +1 -0
  206. package/dist/validation/input-schema.d.ts +26 -0
  207. package/dist/validation/input-schema.d.ts.map +1 -0
  208. package/dist/validation/input-schema.js +67 -0
  209. package/dist/validation/input-schema.js.map +1 -0
  210. package/dist/workflow/registrar.d.ts +20 -0
  211. package/dist/workflow/registrar.d.ts.map +1 -0
  212. package/dist/workflow/registrar.js +61 -0
  213. package/dist/workflow/registrar.js.map +1 -0
  214. package/dist/workflow/template-resolver.d.ts +25 -0
  215. package/dist/workflow/template-resolver.d.ts.map +1 -0
  216. package/dist/workflow/template-resolver.js +112 -0
  217. package/dist/workflow/template-resolver.js.map +1 -0
  218. package/dist/workflow/yaml-loader.d.ts +15 -0
  219. package/dist/workflow/yaml-loader.d.ts.map +1 -0
  220. package/dist/workflow/yaml-loader.js +408 -0
  221. package/dist/workflow/yaml-loader.js.map +1 -0
  222. package/package.json +61 -0
@@ -0,0 +1,751 @@
1
+ // NotionAdapter — wraps the Notion REST API for page, block, and search operations.
2
+ import { WorkflowError } from '../types/workflow-error.js';
3
+ import { parseRetryAfterHeader } from './adapter-utils.js';
4
+ const NOTION_API_VERSION = '2026-03-11';
5
+ const NOTION_BASE_URL = 'https://api.notion.com';
6
+ /**
7
+ * NotionAdapter wraps the Notion REST API.
8
+ *
9
+ * Supported operations:
10
+ * fetch('get_page', { page_id, filter_properties? }) — GET /v1/pages/{page_id}
11
+ * fetch('list_block_children', { block_id, start_cursor?, page_size? }) — GET /v1/blocks/{block_id}/children
12
+ * fetch('query_data_source', { data_source_id, ...opts }) — POST /v1/data_sources/{data_source_id}/query
13
+ * fetch('search', { query?, filter?, sort?, start_cursor?, page_size? }) — POST /v1/search
14
+ * create('create_page', { parent, properties?, children?, icon?, cover? }) — POST /v1/pages
15
+ * create('append_block_children', { block_id, children, position? }) — PATCH /v1/blocks/{block_id}/children
16
+ * update('update_page', { page_id, properties?, icon?, cover?, ... }) — PATCH /v1/pages/{page_id}
17
+ * delete('delete_block', { block_id }) — DELETE /v1/blocks/{block_id}
18
+ */
19
+ export class NotionAdapter {
20
+ id;
21
+ apiKey;
22
+ baseUrl;
23
+ constructor(id, config) {
24
+ if (!config.api_key) {
25
+ throw new Error('NotionAdapter: api_key must not be empty');
26
+ }
27
+ this.id = id;
28
+ this.apiKey = config.api_key;
29
+ this.baseUrl = config.base_url?.replace(/\/$/, '') ?? NOTION_BASE_URL;
30
+ }
31
+ checkAborted(signal) {
32
+ if (signal?.aborted === true) {
33
+ throw new WorkflowError('Adapter request aborted', {
34
+ code: 'STEP_ABORTED',
35
+ category: 'ENGINE',
36
+ agentAction: 'report_to_user',
37
+ retryable: false,
38
+ });
39
+ }
40
+ }
41
+ buildHeaders() {
42
+ return {
43
+ Authorization: `Bearer ${this.apiKey}`,
44
+ 'Notion-Version': NOTION_API_VERSION,
45
+ Accept: 'application/json',
46
+ };
47
+ }
48
+ buildUrl(path) {
49
+ return new URL(path, this.baseUrl);
50
+ }
51
+ /**
52
+ * @param networkRetryable — set to false for non-idempotent write operations
53
+ * (create_page, append_block_children) where retrying a failed request may
54
+ * create duplicate resources.
55
+ */
56
+ async executeRequest(url, method, body, signal, networkRetryable = true) {
57
+ this.checkAborted(signal);
58
+ const hasBody = (method === 'POST' || method === 'PATCH') && body !== undefined;
59
+ const headers = {
60
+ ...this.buildHeaders(),
61
+ ...(hasBody ? { 'Content-Type': 'application/json' } : {}),
62
+ };
63
+ let response;
64
+ try {
65
+ response = await fetch(url.href, {
66
+ method,
67
+ headers,
68
+ ...(hasBody ? { body: JSON.stringify(body) } : {}),
69
+ signal: signal ?? null,
70
+ });
71
+ }
72
+ catch (err) {
73
+ if (err instanceof Error && err.name === 'AbortError') {
74
+ throw new WorkflowError('Adapter request aborted', {
75
+ code: 'STEP_ABORTED',
76
+ category: 'ENGINE',
77
+ agentAction: 'report_to_user',
78
+ retryable: false,
79
+ });
80
+ }
81
+ const message = err instanceof Error ? err.message : String(err);
82
+ throw new WorkflowError(message, {
83
+ code: 'NETWORK_UNREACHABLE',
84
+ category: 'NETWORK',
85
+ agentAction: 'wait_for_human',
86
+ retryable: networkRetryable,
87
+ });
88
+ }
89
+ return response;
90
+ }
91
+ async throwHttpError(response, operation) {
92
+ // Read body as text first — avoids "body already consumed" if JSON.parse fails.
93
+ const rawText = await response.text();
94
+ let parsedBody;
95
+ try {
96
+ parsedBody = JSON.parse(rawText);
97
+ }
98
+ catch {
99
+ parsedBody = rawText;
100
+ }
101
+ const status = response.status;
102
+ const notionRequestIdHeader = response.headers.get('x-notion-request-id');
103
+ const baseDetails = { status, operation };
104
+ if (notionRequestIdHeader !== null) {
105
+ baseDetails['notionRequestId'] = notionRequestIdHeader;
106
+ }
107
+ if (status === 400) {
108
+ const parsed = parsedBody;
109
+ const apiMessage = typeof parsed?.['message'] === 'string' ? parsed['message'] : 'validation error';
110
+ throw new WorkflowError(`Notion API error (HTTP 400): ${apiMessage}`, {
111
+ code: 'SERVICE_HTTP_4XX',
112
+ category: 'SERVICE',
113
+ agentAction: 'stop',
114
+ retryable: false,
115
+ details: { ...baseDetails, body: parsedBody },
116
+ });
117
+ }
118
+ if (status === 401) {
119
+ throw new WorkflowError('Notion authentication failed — check API key', {
120
+ code: 'SERVICE_AUTH_FAILED',
121
+ category: 'SERVICE',
122
+ agentAction: 'stop',
123
+ retryable: false,
124
+ details: baseDetails,
125
+ });
126
+ }
127
+ if (status === 403) {
128
+ throw new WorkflowError('Notion: forbidden (HTTP 403)', {
129
+ code: 'SERVICE_HTTP_4XX',
130
+ category: 'SERVICE',
131
+ agentAction: 'stop',
132
+ retryable: false,
133
+ details: baseDetails,
134
+ });
135
+ }
136
+ if (status === 404) {
137
+ throw new WorkflowError('Notion: resource not found', {
138
+ code: 'SERVICE_NOT_FOUND',
139
+ category: 'SERVICE',
140
+ agentAction: 'provide_input',
141
+ retryable: false,
142
+ details: baseDetails,
143
+ });
144
+ }
145
+ if (status === 409) {
146
+ const parsed = parsedBody;
147
+ const apiMessage = typeof parsed?.['message'] === 'string' ? parsed['message'] : 'conflict';
148
+ throw new WorkflowError(`Notion API error (HTTP 409): ${apiMessage}`, {
149
+ code: 'SERVICE_HTTP_4XX',
150
+ category: 'SERVICE',
151
+ agentAction: 'stop',
152
+ retryable: false,
153
+ details: { ...baseDetails, body: parsedBody },
154
+ });
155
+ }
156
+ if (status === 429) {
157
+ const retryAfterSeconds = parseRetryAfterHeader(response.headers.get('Retry-After'));
158
+ throw new WorkflowError('Rate limited by Notion API', {
159
+ code: 'SERVICE_RATE_LIMITED',
160
+ category: 'SERVICE',
161
+ agentAction: 'wait_for_human',
162
+ retryable: true,
163
+ details: {
164
+ ...baseDetails,
165
+ ...(retryAfterSeconds !== undefined ? { retryAfterSeconds } : {}),
166
+ },
167
+ });
168
+ }
169
+ if (status === 503) {
170
+ const parsed = parsedBody;
171
+ const additionalData = parsed?.['additional_data'];
172
+ throw new WorkflowError('Notion service temporarily unavailable (HTTP 503)', {
173
+ code: 'SERVICE_HTTP_5XX',
174
+ category: 'SERVICE',
175
+ agentAction: 'wait_for_human',
176
+ retryable: true,
177
+ details: {
178
+ ...baseDetails,
179
+ ...(additionalData !== undefined ? { additionalData } : {}),
180
+ },
181
+ });
182
+ }
183
+ if (status >= 500) {
184
+ throw new WorkflowError(`Notion server error (HTTP ${status})`, {
185
+ code: 'SERVICE_HTTP_5XX',
186
+ category: 'SERVICE',
187
+ agentAction: 'report_to_user',
188
+ retryable: true,
189
+ details: baseDetails,
190
+ });
191
+ }
192
+ // other 4xx
193
+ throw new WorkflowError(`Notion: HTTP ${status}`, {
194
+ code: 'SERVICE_HTTP_4XX',
195
+ category: 'SERVICE',
196
+ agentAction: 'stop',
197
+ retryable: false,
198
+ details: baseDetails,
199
+ });
200
+ }
201
+ async fetch(operation, params, _config, signal) {
202
+ if (operation === 'get_page') {
203
+ const pageId = params['page_id'];
204
+ if (typeof pageId !== 'string' || pageId === '') {
205
+ throw new WorkflowError('NotionAdapter: page_id param must be a non-empty string', {
206
+ code: 'ADAPTER_VALIDATION_FAILED',
207
+ category: 'ENGINE',
208
+ agentAction: 'provide_input',
209
+ retryable: false,
210
+ });
211
+ }
212
+ const url = this.buildUrl(`/v1/pages/${encodeURIComponent(pageId)}`);
213
+ const filterProperties = params['filter_properties'];
214
+ if (Array.isArray(filterProperties) && filterProperties.length > 0) {
215
+ for (const val of filterProperties) {
216
+ url.searchParams.append('filter_properties', String(val));
217
+ }
218
+ }
219
+ const response = await this.executeRequest(url, 'GET', undefined, signal);
220
+ if (!response.ok) {
221
+ await this.throwHttpError(response, operation);
222
+ }
223
+ let parsed;
224
+ try {
225
+ parsed = await response.json();
226
+ }
227
+ catch {
228
+ throw new WorkflowError('NotionAdapter: failed to parse response body', {
229
+ code: 'SERVICE_RESPONSE_INVALID',
230
+ category: 'SERVICE',
231
+ agentAction: 'report_to_user',
232
+ retryable: false,
233
+ });
234
+ }
235
+ const data = parsed;
236
+ if (typeof data.id !== 'string' || data.id === '') {
237
+ throw new WorkflowError('NotionAdapter: get_page response missing id field', {
238
+ code: 'SERVICE_RESPONSE_INVALID',
239
+ category: 'SERVICE',
240
+ agentAction: 'report_to_user',
241
+ retryable: false,
242
+ });
243
+ }
244
+ return { status: response.status, data: parsed };
245
+ }
246
+ if (operation === 'list_block_children') {
247
+ // data shape for paginated responses:
248
+ // { object: 'list', results: unknown[], has_more: boolean, next_cursor: string | null }
249
+ // Pass next_cursor back as start_cursor on the next call to retrieve subsequent pages.
250
+ const blockId = params['block_id'];
251
+ if (typeof blockId !== 'string' || blockId === '') {
252
+ throw new WorkflowError('NotionAdapter: block_id param must be a non-empty string', {
253
+ code: 'ADAPTER_VALIDATION_FAILED',
254
+ category: 'ENGINE',
255
+ agentAction: 'provide_input',
256
+ retryable: false,
257
+ });
258
+ }
259
+ const url = this.buildUrl(`/v1/blocks/${encodeURIComponent(blockId)}/children`);
260
+ if (typeof params['start_cursor'] === 'string') {
261
+ url.searchParams.set('start_cursor', params['start_cursor']);
262
+ }
263
+ if (typeof params['page_size'] === 'number') {
264
+ url.searchParams.set('page_size', String(params['page_size']));
265
+ }
266
+ const response = await this.executeRequest(url, 'GET', undefined, signal);
267
+ if (!response.ok) {
268
+ await this.throwHttpError(response, operation);
269
+ }
270
+ let parsed;
271
+ try {
272
+ parsed = await response.json();
273
+ }
274
+ catch {
275
+ throw new WorkflowError('NotionAdapter: failed to parse response body', {
276
+ code: 'SERVICE_RESPONSE_INVALID',
277
+ category: 'SERVICE',
278
+ agentAction: 'report_to_user',
279
+ retryable: false,
280
+ });
281
+ }
282
+ const data = parsed;
283
+ if (!Array.isArray(data.results)) {
284
+ throw new WorkflowError('NotionAdapter: list_block_children response missing results array', {
285
+ code: 'SERVICE_RESPONSE_INVALID',
286
+ category: 'SERVICE',
287
+ agentAction: 'report_to_user',
288
+ retryable: false,
289
+ });
290
+ }
291
+ return { status: response.status, data: parsed };
292
+ }
293
+ if (operation === 'query_data_source') {
294
+ // data shape for paginated responses:
295
+ // { object: 'list', results: unknown[], has_more: boolean, next_cursor: string | null }
296
+ // Pass next_cursor back as start_cursor on the next call to retrieve subsequent pages.
297
+ const dataSourceId = params['data_source_id'];
298
+ if (typeof dataSourceId !== 'string' || dataSourceId === '') {
299
+ throw new WorkflowError('NotionAdapter: data_source_id param must be a non-empty string', {
300
+ code: 'ADAPTER_VALIDATION_FAILED',
301
+ category: 'ENGINE',
302
+ agentAction: 'provide_input',
303
+ retryable: false,
304
+ });
305
+ }
306
+ const url = this.buildUrl(`/v1/data_sources/${encodeURIComponent(dataSourceId)}/query`);
307
+ const filterProperties = params['filter_properties'];
308
+ if (Array.isArray(filterProperties) && filterProperties.length > 0) {
309
+ for (const val of filterProperties) {
310
+ url.searchParams.append('filter_properties', String(val));
311
+ }
312
+ }
313
+ const body = {};
314
+ if (params['filter'] !== undefined)
315
+ body['filter'] = params['filter'];
316
+ if (params['sorts'] !== undefined)
317
+ body['sorts'] = params['sorts'];
318
+ if (params['start_cursor'] !== undefined)
319
+ body['start_cursor'] = params['start_cursor'];
320
+ if (params['page_size'] !== undefined)
321
+ body['page_size'] = params['page_size'];
322
+ if (params['in_trash'] !== undefined)
323
+ body['in_trash'] = params['in_trash'];
324
+ const response = await this.executeRequest(url, 'POST', body, signal);
325
+ if (!response.ok) {
326
+ await this.throwHttpError(response, operation);
327
+ }
328
+ let parsed;
329
+ try {
330
+ parsed = await response.json();
331
+ }
332
+ catch {
333
+ throw new WorkflowError('NotionAdapter: failed to parse response body', {
334
+ code: 'SERVICE_RESPONSE_INVALID',
335
+ category: 'SERVICE',
336
+ agentAction: 'report_to_user',
337
+ retryable: false,
338
+ });
339
+ }
340
+ const data = parsed;
341
+ if (!Array.isArray(data.results)) {
342
+ throw new WorkflowError('NotionAdapter: query_data_source response missing results array', {
343
+ code: 'SERVICE_RESPONSE_INVALID',
344
+ category: 'SERVICE',
345
+ agentAction: 'report_to_user',
346
+ retryable: false,
347
+ });
348
+ }
349
+ return { status: response.status, data: parsed };
350
+ }
351
+ if (operation === 'search') {
352
+ // data shape for paginated responses:
353
+ // { object: 'list', results: unknown[], has_more: boolean, next_cursor: string | null }
354
+ // Pass next_cursor back as start_cursor on the next call to retrieve subsequent pages.
355
+ // Validate filter structure before sending.
356
+ const filter = params['filter'];
357
+ if (filter !== undefined) {
358
+ if (filter === null || typeof filter !== 'object' || Array.isArray(filter)) {
359
+ throw new WorkflowError("NotionAdapter: search — 'filter' must be a plain object with property: 'object' and value: 'page'|'database'|'data_source'", {
360
+ code: 'ADAPTER_VALIDATION_FAILED',
361
+ category: 'ENGINE',
362
+ agentAction: 'provide_input',
363
+ retryable: false,
364
+ });
365
+ }
366
+ const f = filter;
367
+ if (f['property'] !== 'object' ||
368
+ !['page', 'database', 'data_source'].includes(f['value'])) {
369
+ throw new WorkflowError("NotionAdapter: search — 'filter' must have property: 'object' and value: 'page'|'database'|'data_source'", {
370
+ code: 'ADAPTER_VALIDATION_FAILED',
371
+ category: 'ENGINE',
372
+ agentAction: 'provide_input',
373
+ retryable: false,
374
+ });
375
+ }
376
+ }
377
+ const url = this.buildUrl('/v1/search');
378
+ const body = {};
379
+ if (params['query'] !== undefined)
380
+ body['query'] = params['query'];
381
+ if (params['filter'] !== undefined)
382
+ body['filter'] = params['filter'];
383
+ if (params['sort'] !== undefined)
384
+ body['sort'] = params['sort'];
385
+ if (params['start_cursor'] !== undefined)
386
+ body['start_cursor'] = params['start_cursor'];
387
+ if (params['page_size'] !== undefined)
388
+ body['page_size'] = params['page_size'];
389
+ const response = await this.executeRequest(url, 'POST', body, signal);
390
+ if (!response.ok) {
391
+ await this.throwHttpError(response, operation);
392
+ }
393
+ let parsed;
394
+ try {
395
+ parsed = await response.json();
396
+ }
397
+ catch {
398
+ throw new WorkflowError('NotionAdapter: failed to parse response body', {
399
+ code: 'SERVICE_RESPONSE_INVALID',
400
+ category: 'SERVICE',
401
+ agentAction: 'report_to_user',
402
+ retryable: false,
403
+ });
404
+ }
405
+ const data = parsed;
406
+ if (!Array.isArray(data.results)) {
407
+ throw new WorkflowError('NotionAdapter: search response missing results array', {
408
+ code: 'SERVICE_RESPONSE_INVALID',
409
+ category: 'SERVICE',
410
+ agentAction: 'report_to_user',
411
+ retryable: false,
412
+ });
413
+ }
414
+ return { status: response.status, data: parsed };
415
+ }
416
+ throw new WorkflowError(`NotionAdapter: unsupported fetch operation "${operation}"`, {
417
+ code: 'ADAPTER_OP_UNSUPPORTED',
418
+ category: 'ENGINE',
419
+ agentAction: 'report_to_user',
420
+ retryable: false,
421
+ });
422
+ }
423
+ /**
424
+ * NOTE: retryable is false for network errors — retrying a failed create or append
425
+ * risks creating duplicate content if the server processed the request before the network dropped.
426
+ */
427
+ async create(operation, params, _config, signal) {
428
+ if (operation === 'create_page') {
429
+ const parent = params['parent'];
430
+ if (parent === null ||
431
+ parent === undefined ||
432
+ typeof parent !== 'object' ||
433
+ Array.isArray(parent)) {
434
+ throw new WorkflowError('NotionAdapter: create_page — parent must be a plain object', {
435
+ code: 'ADAPTER_VALIDATION_FAILED',
436
+ category: 'ENGINE',
437
+ agentAction: 'provide_input',
438
+ retryable: false,
439
+ });
440
+ }
441
+ const p = parent;
442
+ let wireParent;
443
+ if (typeof p['page_id'] === 'string' && p['page_id'] !== '') {
444
+ wireParent = { type: 'page_id', page_id: p['page_id'] };
445
+ }
446
+ else if (typeof p['data_source_id'] === 'string' && p['data_source_id'] !== '') {
447
+ wireParent = { type: 'data_source_id', data_source_id: p['data_source_id'] };
448
+ }
449
+ else if (p['workspace'] === true) {
450
+ wireParent = { type: 'workspace', workspace: true };
451
+ }
452
+ else {
453
+ throw new WorkflowError("NotionAdapter: create_page — 'parent' must be one of: { page_id }, { data_source_id }, or { workspace: true }", {
454
+ code: 'ADAPTER_VALIDATION_FAILED',
455
+ category: 'ENGINE',
456
+ agentAction: 'provide_input',
457
+ retryable: false,
458
+ });
459
+ }
460
+ const children = params['children'];
461
+ const markdown = params['markdown'];
462
+ if (children !== undefined && markdown !== undefined) {
463
+ throw new WorkflowError("NotionAdapter: create_page — 'children' and 'markdown' are mutually exclusive", {
464
+ code: 'ADAPTER_VALIDATION_FAILED',
465
+ category: 'ENGINE',
466
+ agentAction: 'provide_input',
467
+ retryable: false,
468
+ });
469
+ }
470
+ if (Array.isArray(children) && children.length > 100) {
471
+ throw new WorkflowError(`NotionAdapter: create_page — 'children' array exceeds 100 items (got ${children.length})`, {
472
+ code: 'ADAPTER_VALIDATION_FAILED',
473
+ category: 'ENGINE',
474
+ agentAction: 'provide_input',
475
+ retryable: false,
476
+ });
477
+ }
478
+ const body = { parent: wireParent };
479
+ if (params['properties'] !== undefined)
480
+ body['properties'] = params['properties'];
481
+ if (params['children'] !== undefined)
482
+ body['children'] = params['children'];
483
+ if (params['markdown'] !== undefined)
484
+ body['markdown'] = params['markdown'];
485
+ if (params['icon'] !== undefined)
486
+ body['icon'] = params['icon'];
487
+ if (params['cover'] !== undefined)
488
+ body['cover'] = params['cover'];
489
+ const url = this.buildUrl('/v1/pages');
490
+ // networkRetryable: false — retrying risks creating a duplicate page.
491
+ const response = await this.executeRequest(url, 'POST', body, signal, false);
492
+ if (!response.ok) {
493
+ await this.throwHttpError(response, operation);
494
+ }
495
+ let parsed;
496
+ try {
497
+ parsed = await response.json();
498
+ }
499
+ catch {
500
+ throw new WorkflowError('NotionAdapter: failed to parse response body', {
501
+ code: 'SERVICE_RESPONSE_INVALID',
502
+ category: 'SERVICE',
503
+ agentAction: 'report_to_user',
504
+ retryable: false,
505
+ });
506
+ }
507
+ const data = parsed;
508
+ if (typeof data.id !== 'string' || data.id === '') {
509
+ throw new WorkflowError('NotionAdapter: create_page response missing id field', {
510
+ code: 'SERVICE_RESPONSE_INVALID',
511
+ category: 'SERVICE',
512
+ agentAction: 'report_to_user',
513
+ retryable: false,
514
+ });
515
+ }
516
+ return { status: response.status, data: parsed };
517
+ }
518
+ if (operation === 'append_block_children') {
519
+ // data shape for paginated responses:
520
+ // { object: 'list', results: unknown[], has_more: boolean, next_cursor: string | null }
521
+ // Pass next_cursor back as start_cursor on the next call to retrieve subsequent pages.
522
+ //
523
+ // Note: append_block_children lives in create() because it creates new block resources.
524
+ // The underlying HTTP verb is PATCH — this is expected.
525
+ const blockId = params['block_id'];
526
+ if (typeof blockId !== 'string' || blockId === '') {
527
+ throw new WorkflowError('NotionAdapter: block_id param must be a non-empty string', {
528
+ code: 'ADAPTER_VALIDATION_FAILED',
529
+ category: 'ENGINE',
530
+ agentAction: 'provide_input',
531
+ retryable: false,
532
+ });
533
+ }
534
+ const children = params['children'];
535
+ if (!Array.isArray(children) || children.length === 0) {
536
+ throw new WorkflowError('NotionAdapter: append_block_children — children must be a non-empty array', {
537
+ code: 'ADAPTER_VALIDATION_FAILED',
538
+ category: 'ENGINE',
539
+ agentAction: 'provide_input',
540
+ retryable: false,
541
+ });
542
+ }
543
+ if (children.length > 100) {
544
+ throw new WorkflowError(`NotionAdapter: append_block_children — 'children' array exceeds 100 items (got ${children.length})`, {
545
+ code: 'ADAPTER_VALIDATION_FAILED',
546
+ category: 'ENGINE',
547
+ agentAction: 'provide_input',
548
+ retryable: false,
549
+ });
550
+ }
551
+ for (const item of children) {
552
+ if (item === null ||
553
+ typeof item !== 'object' ||
554
+ Array.isArray(item) ||
555
+ typeof item['type'] !== 'string' ||
556
+ item['type'] === '') {
557
+ throw new WorkflowError('NotionAdapter: append_block_children — each child must be a plain object with a non-empty type field', {
558
+ code: 'ADAPTER_VALIDATION_FAILED',
559
+ category: 'ENGINE',
560
+ agentAction: 'provide_input',
561
+ retryable: false,
562
+ });
563
+ }
564
+ }
565
+ const position = params['position'];
566
+ if (position !== undefined) {
567
+ if (position === null || typeof position !== 'object' || Array.isArray(position)) {
568
+ throw new WorkflowError("NotionAdapter: append_block_children — 'position' must be a plain object", {
569
+ code: 'ADAPTER_VALIDATION_FAILED',
570
+ category: 'ENGINE',
571
+ agentAction: 'provide_input',
572
+ retryable: false,
573
+ });
574
+ }
575
+ const pos = position;
576
+ if (!['end', 'start', 'after_block'].includes(pos['type'])) {
577
+ throw new WorkflowError("NotionAdapter: append_block_children — 'position.type' must be one of 'end', 'start', 'after_block'", {
578
+ code: 'ADAPTER_VALIDATION_FAILED',
579
+ category: 'ENGINE',
580
+ agentAction: 'provide_input',
581
+ retryable: false,
582
+ });
583
+ }
584
+ if (pos['type'] === 'after_block') {
585
+ const afterBlock = pos['after_block'];
586
+ if (afterBlock === null ||
587
+ typeof afterBlock !== 'object' ||
588
+ Array.isArray(afterBlock) ||
589
+ typeof afterBlock['id'] !== 'string' ||
590
+ afterBlock['id'] === '') {
591
+ throw new WorkflowError("NotionAdapter: append_block_children — 'position.after_block.id' must be a non-empty string when type is 'after_block'", {
592
+ code: 'ADAPTER_VALIDATION_FAILED',
593
+ category: 'ENGINE',
594
+ agentAction: 'provide_input',
595
+ retryable: false,
596
+ });
597
+ }
598
+ }
599
+ }
600
+ const body = { children };
601
+ if (position !== undefined)
602
+ body['position'] = position;
603
+ const url = this.buildUrl(`/v1/blocks/${encodeURIComponent(blockId)}/children`);
604
+ // networkRetryable: false — retrying risks appending duplicate blocks.
605
+ const response = await this.executeRequest(url, 'PATCH', body, signal, false);
606
+ if (!response.ok) {
607
+ await this.throwHttpError(response, operation);
608
+ }
609
+ let parsed;
610
+ try {
611
+ parsed = await response.json();
612
+ }
613
+ catch {
614
+ throw new WorkflowError('NotionAdapter: failed to parse response body', {
615
+ code: 'SERVICE_RESPONSE_INVALID',
616
+ category: 'SERVICE',
617
+ agentAction: 'report_to_user',
618
+ retryable: false,
619
+ });
620
+ }
621
+ const data = parsed;
622
+ if (!Array.isArray(data.results)) {
623
+ throw new WorkflowError('NotionAdapter: append_block_children response missing results array', {
624
+ code: 'SERVICE_RESPONSE_INVALID',
625
+ category: 'SERVICE',
626
+ agentAction: 'report_to_user',
627
+ retryable: false,
628
+ });
629
+ }
630
+ return { status: response.status, data: parsed };
631
+ }
632
+ throw new WorkflowError(`NotionAdapter: unsupported create operation "${operation}"`, {
633
+ code: 'ADAPTER_OP_UNSUPPORTED',
634
+ category: 'ENGINE',
635
+ agentAction: 'report_to_user',
636
+ retryable: false,
637
+ });
638
+ }
639
+ async update(operation, params, _config, signal) {
640
+ if (operation === 'update_page') {
641
+ const pageId = params['page_id'];
642
+ if (typeof pageId !== 'string' || pageId === '') {
643
+ throw new WorkflowError('NotionAdapter: page_id param must be a non-empty string', {
644
+ code: 'ADAPTER_VALIDATION_FAILED',
645
+ category: 'ENGINE',
646
+ agentAction: 'provide_input',
647
+ retryable: false,
648
+ });
649
+ }
650
+ if ('archived' in params) {
651
+ throw new WorkflowError("NotionAdapter: update_page — 'archived' is deprecated; use 'in_trash' instead", {
652
+ code: 'ADAPTER_VALIDATION_FAILED',
653
+ category: 'ENGINE',
654
+ agentAction: 'provide_input',
655
+ retryable: false,
656
+ });
657
+ }
658
+ const body = {};
659
+ if (params['properties'] !== undefined)
660
+ body['properties'] = params['properties'];
661
+ if (params['icon'] !== undefined)
662
+ body['icon'] = params['icon'];
663
+ if (params['cover'] !== undefined)
664
+ body['cover'] = params['cover'];
665
+ if (params['in_trash'] !== undefined)
666
+ body['in_trash'] = params['in_trash'];
667
+ if (params['is_locked'] !== undefined)
668
+ body['is_locked'] = params['is_locked'];
669
+ const url = this.buildUrl(`/v1/pages/${encodeURIComponent(pageId)}`);
670
+ const response = await this.executeRequest(url, 'PATCH', body, signal);
671
+ if (!response.ok) {
672
+ await this.throwHttpError(response, operation);
673
+ }
674
+ let parsed;
675
+ try {
676
+ parsed = await response.json();
677
+ }
678
+ catch {
679
+ throw new WorkflowError('NotionAdapter: failed to parse response body', {
680
+ code: 'SERVICE_RESPONSE_INVALID',
681
+ category: 'SERVICE',
682
+ agentAction: 'report_to_user',
683
+ retryable: false,
684
+ });
685
+ }
686
+ const data = parsed;
687
+ if (typeof data.id !== 'string' || data.id === '') {
688
+ throw new WorkflowError('NotionAdapter: update_page response missing id field', {
689
+ code: 'SERVICE_RESPONSE_INVALID',
690
+ category: 'SERVICE',
691
+ agentAction: 'report_to_user',
692
+ retryable: false,
693
+ });
694
+ }
695
+ return { status: response.status, data: parsed };
696
+ }
697
+ throw new WorkflowError(`NotionAdapter: unsupported update operation "${operation}"`, {
698
+ code: 'ADAPTER_OP_UNSUPPORTED',
699
+ category: 'ENGINE',
700
+ agentAction: 'report_to_user',
701
+ retryable: false,
702
+ });
703
+ }
704
+ async delete(operation, params, _config, signal) {
705
+ if (operation === 'delete_block') {
706
+ const blockId = params['block_id'];
707
+ if (typeof blockId !== 'string' || blockId === '') {
708
+ throw new WorkflowError('NotionAdapter: block_id param must be a non-empty string', {
709
+ code: 'ADAPTER_VALIDATION_FAILED',
710
+ category: 'ENGINE',
711
+ agentAction: 'provide_input',
712
+ retryable: false,
713
+ });
714
+ }
715
+ const url = this.buildUrl(`/v1/blocks/${encodeURIComponent(blockId)}`);
716
+ const response = await this.executeRequest(url, 'DELETE', undefined, signal);
717
+ if (!response.ok) {
718
+ await this.throwHttpError(response, operation);
719
+ }
720
+ let parsed;
721
+ try {
722
+ parsed = await response.json();
723
+ }
724
+ catch {
725
+ throw new WorkflowError('NotionAdapter: failed to parse response body', {
726
+ code: 'SERVICE_RESPONSE_INVALID',
727
+ category: 'SERVICE',
728
+ agentAction: 'report_to_user',
729
+ retryable: false,
730
+ });
731
+ }
732
+ const data = parsed;
733
+ if (typeof data.id !== 'string' || data.id === '') {
734
+ throw new WorkflowError('NotionAdapter: delete_block response missing id field', {
735
+ code: 'SERVICE_RESPONSE_INVALID',
736
+ category: 'SERVICE',
737
+ agentAction: 'report_to_user',
738
+ retryable: false,
739
+ });
740
+ }
741
+ return { status: response.status, data: parsed };
742
+ }
743
+ throw new WorkflowError(`NotionAdapter: unsupported delete operation "${operation}"`, {
744
+ code: 'ADAPTER_OP_UNSUPPORTED',
745
+ category: 'ENGINE',
746
+ agentAction: 'report_to_user',
747
+ retryable: false,
748
+ });
749
+ }
750
+ }
751
+ //# sourceMappingURL=notion-adapter.js.map