@noodleseed/one 0.174.0 → 0.175.1

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 (164) hide show
  1. package/dist/commands/solutions-activity.js +1 -1
  2. package/dist/commands/solutions-activity.js.map +1 -1
  3. package/dist/commands/solutions-drafts.d.ts.map +1 -1
  4. package/dist/commands/solutions-drafts.js +36 -9
  5. package/dist/commands/solutions-drafts.js.map +1 -1
  6. package/dist/commands/solutions-native-lifecycle.d.ts +5 -0
  7. package/dist/commands/solutions-native-lifecycle.d.ts.map +1 -0
  8. package/dist/commands/solutions-native-lifecycle.js +77 -0
  9. package/dist/commands/solutions-native-lifecycle.js.map +1 -0
  10. package/dist/commands/solutions-ops.d.ts.map +1 -1
  11. package/dist/commands/solutions-ops.js +3 -0
  12. package/dist/commands/solutions-ops.js.map +1 -1
  13. package/dist/commands/solutions-workspace.d.ts.map +1 -1
  14. package/dist/commands/solutions-workspace.js +36 -11
  15. package/dist/commands/solutions-workspace.js.map +1 -1
  16. package/dist/commands/whatsapp-output.d.ts.map +1 -1
  17. package/dist/commands/whatsapp-output.js +20 -4
  18. package/dist/commands/whatsapp-output.js.map +1 -1
  19. package/node_modules/@noodle-borg/admission-limits/dist/public-record-admission.d.ts +16 -9
  20. package/node_modules/@noodle-borg/admission-limits/dist/public-record-admission.js +5 -2
  21. package/node_modules/@noodle-borg/agent-kit/dist/curated/error-fixes.js +9 -0
  22. package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +5 -5
  23. package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js +9 -0
  24. package/node_modules/@noodle-borg/agent-kit/dist/skill-authoring-refs.js +1 -1
  25. package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.js +13 -1
  26. package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
  27. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.d.ts +5 -3
  28. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-coordinator.js +5 -4
  29. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-inbox.js +6 -2
  30. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-store.d.ts +1 -1
  31. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-types.d.ts +12 -0
  32. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-work.d.ts +7 -2
  33. package/node_modules/@noodle-borg/assistant-gateway/dist/channel-work.js +14 -4
  34. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-capture.js +104 -0
  35. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-judgment.js +93 -0
  36. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-ledger.d.ts +105 -0
  37. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-ledger.js +183 -0
  38. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-model-call.js +38 -0
  39. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-parsers.d.ts +22 -0
  40. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-parsers.js +109 -0
  41. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-review.d.ts +21 -0
  42. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-review.js +44 -0
  43. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-spec.d.ts +18 -0
  44. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-spec.js +86 -0
  45. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-store.d.ts +11 -0
  46. package/node_modules/@noodle-borg/assistant-gateway/dist/collection-store.js +18 -0
  47. package/node_modules/@noodle-borg/assistant-gateway/dist/messaging-tool-dispatch.d.ts +20 -1
  48. package/node_modules/@noodle-borg/assistant-gateway/dist/messaging-tool-dispatch.js +38 -2
  49. package/node_modules/@noodle-borg/assistant-gateway/dist/model-runtime.js +3 -0
  50. package/node_modules/@noodle-borg/assistant-gateway/dist/portable.d.ts +5 -0
  51. package/node_modules/@noodle-borg/assistant-gateway/dist/portable.js +5 -0
  52. package/node_modules/@noodle-borg/assistant-gateway/package.json +1 -1
  53. package/node_modules/@noodle-borg/authoring/dist/index.d.ts +1 -0
  54. package/node_modules/@noodle-borg/authoring/dist/interaction.d.ts +49 -0
  55. package/node_modules/@noodle-borg/authoring/dist/interaction.js +25 -0
  56. package/node_modules/@noodle-borg/authoring/dist/server.d.ts +3 -0
  57. package/node_modules/@noodle-borg/authoring/dist/server.js +4 -0
  58. package/node_modules/@noodle-borg/authoring/package.json +6 -1
  59. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-channels.js +1 -1
  60. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solution-drafts.js +7 -0
  61. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solution-native-lifecycle.js +43 -0
  62. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solution-records.js +154 -0
  63. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solution-workspace.js +8 -1
  64. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-solutions.js +2 -149
  65. package/node_modules/@noodle-borg/compiler/dist/artifact/assemble.js +2 -1
  66. package/node_modules/@noodle-borg/compiler/dist/artifact/types.d.ts +33 -0
  67. package/node_modules/@noodle-borg/compiler/dist/artifact/version.d.ts +2 -1
  68. package/node_modules/@noodle-borg/compiler/dist/artifact/version.js +2 -1
  69. package/node_modules/@noodle-borg/compiler/dist/compile.js +5 -0
  70. package/node_modules/@noodle-borg/compiler/dist/errors.d.ts +1 -1
  71. package/node_modules/@noodle-borg/compiler/dist/index.d.ts +2 -2
  72. package/node_modules/@noodle-borg/compiler/dist/index.js +1 -1
  73. package/node_modules/@noodle-borg/compiler/dist/manifest/interaction-schema.d.ts +41 -0
  74. package/node_modules/@noodle-borg/compiler/dist/manifest/interaction-schema.js +62 -0
  75. package/node_modules/@noodle-borg/compiler/dist/manifest/interaction-validation.js +220 -0
  76. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +60 -4
  77. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +7 -1
  78. package/node_modules/@noodle-borg/compiler/dist/manifest/website-projection.d.ts +9 -1
  79. package/node_modules/@noodle-borg/compiler/dist/manifest/website-projection.js +58 -10
  80. package/node_modules/@noodle-borg/control-plane/dist/in-memory-atomic-state.d.ts +8 -0
  81. package/node_modules/@noodle-borg/control-plane/dist/in-memory-atomic-state.js +136 -0
  82. package/node_modules/@noodle-borg/control-plane/dist/in-memory-control-plane-store.d.ts +7 -0
  83. package/node_modules/@noodle-borg/control-plane/dist/in-memory-control-plane-store.js +16 -24
  84. package/node_modules/@noodle-borg/control-plane/dist/in-memory-organization-store.d.ts +4 -2
  85. package/node_modules/@noodle-borg/control-plane/dist/in-memory-organization-store.js +8 -6
  86. package/node_modules/@noodle-borg/control-plane/dist/mcp-subdomain-claims.d.ts +2 -1
  87. package/node_modules/@noodle-borg/control-plane/dist/mcp-subdomain-claims.js +13 -8
  88. package/node_modules/@noodle-borg/control-plane/dist/organization-agreements.d.ts +13 -4
  89. package/node_modules/@noodle-borg/control-plane/dist/organization-agreements.js +23 -7
  90. package/node_modules/@noodle-borg/control-plane/dist/portable.d.ts +2 -1
  91. package/node_modules/@noodle-borg/control-plane/dist/portable.js +1 -0
  92. package/node_modules/@noodle-borg/module/dist/platform-human-identity.d.ts +5 -2
  93. package/node_modules/@noodle-borg/runtime/dist/connector/types.d.ts +16 -4
  94. package/node_modules/@noodle-borg/runtime/dist/execute.d.ts +3 -6
  95. package/node_modules/@noodle-borg/runtime/dist/index.d.ts +1 -1
  96. package/node_modules/@noodle-borg/service/dist/application-activity.js +85 -63
  97. package/node_modules/@noodle-borg/service/dist/application-drafts/contracts.js +9 -0
  98. package/node_modules/@noodle-borg/service/dist/application-drafts/paths.js +2 -2
  99. package/node_modules/@noodle-borg/service/dist/application-runtime-target.js +1 -1
  100. package/node_modules/@noodle-borg/service/dist/business-information/contracts.d.ts +18 -4
  101. package/node_modules/@noodle-borg/service/dist/business-information/in-memory-business-pages.js +3 -5
  102. package/node_modules/@noodle-borg/service/dist/business-information/in-memory-installation-lifecycle.js +8 -6
  103. package/node_modules/@noodle-borg/service/dist/business-information/in-memory-native-lifecycle.js +80 -0
  104. package/node_modules/@noodle-borg/service/dist/business-information/in-memory-store.js +27 -31
  105. package/node_modules/@noodle-borg/service/dist/business-information/memory-native-retention.js +29 -0
  106. package/node_modules/@noodle-borg/service/dist/business-information/model.js +9 -2
  107. package/node_modules/@noodle-borg/service/dist/business-information/native-query.js +3 -1
  108. package/node_modules/@noodle-borg/service/dist/business-information/native-record-lifecycle.d.ts +27 -0
  109. package/node_modules/@noodle-borg/service/dist/business-information/native-record-lifecycle.js +63 -0
  110. package/node_modules/@noodle-borg/service/dist/business-information/principal-authority.d.ts +3 -1
  111. package/node_modules/@noodle-borg/service/dist/business-information/principal-authority.js +9 -4
  112. package/node_modules/@noodle-borg/service/dist/business-information/staff-authority.d.ts +16 -0
  113. package/node_modules/@noodle-borg/service/dist/business-information/staff-authority.js +60 -0
  114. package/node_modules/@noodle-borg/service/dist/business-information/workspace-permissions.js +18 -0
  115. package/node_modules/@noodle-borg/service/dist/business-information-runtime.js +1 -0
  116. package/node_modules/@noodle-borg/service/dist/business-onboarding.js +16 -3
  117. package/node_modules/@noodle-borg/service/dist/business-workspaces/contracts.d.ts +110 -0
  118. package/node_modules/@noodle-borg/service/dist/business-workspaces/permissions.d.ts +7 -0
  119. package/node_modules/@noodle-borg/service/dist/business-workspaces/store.d.ts +102 -0
  120. package/node_modules/@noodle-borg/service/dist/channels/360dialog.js +70 -8
  121. package/node_modules/@noodle-borg/service/dist/channels/capability-report.js +73 -0
  122. package/node_modules/@noodle-borg/service/dist/channels/collection-turn.js +232 -0
  123. package/node_modules/@noodle-borg/service/dist/channels/composition.js +1 -0
  124. package/node_modules/@noodle-borg/service/dist/channels/messaging-eligibility.js +45 -0
  125. package/node_modules/@noodle-borg/service/dist/channels/reply-buttons.js +23 -0
  126. package/node_modules/@noodle-borg/service/dist/channels/runtime.js +127 -33
  127. package/node_modules/@noodle-borg/service/dist/native-record-connector.js +19 -4
  128. package/node_modules/@noodle-borg/service/dist/routes/application-drafts.js +44 -2
  129. package/node_modules/@noodle-borg/service/dist/routes/assistant-agent.js +4 -0
  130. package/node_modules/@noodle-borg/service/dist/routes/business-authoring-dispatch.js +1 -1
  131. package/node_modules/@noodle-borg/service/dist/routes/business-information-access.js +39 -0
  132. package/node_modules/@noodle-borg/service/dist/routes/business-information-activity.js +1 -1
  133. package/node_modules/@noodle-borg/service/dist/routes/business-information-channels.js +2 -2
  134. package/node_modules/@noodle-borg/service/dist/routes/business-information-connections.js +10 -4
  135. package/node_modules/@noodle-borg/service/dist/routes/business-information-dispatch.js +11 -0
  136. package/node_modules/@noodle-borg/service/dist/routes/business-information-installation.js +3 -1
  137. package/node_modules/@noodle-borg/service/dist/routes/business-information-notice.js +6 -2
  138. package/node_modules/@noodle-borg/service/dist/routes/business-information-pages.js +1 -1
  139. package/node_modules/@noodle-borg/service/dist/routes/business-information-paths.js +1 -1
  140. package/node_modules/@noodle-borg/service/dist/routes/business-information-record-create.js +7 -2
  141. package/node_modules/@noodle-borg/service/dist/routes/business-information-record-reads.js +2 -2
  142. package/node_modules/@noodle-borg/service/dist/routes/business-information-records.js +332 -0
  143. package/node_modules/@noodle-borg/service/dist/routes/business-information-settings.js +6 -3
  144. package/node_modules/@noodle-borg/service/dist/routes/business-information-staff.js +9 -5
  145. package/node_modules/@noodle-borg/service/dist/routes/business-information.js +12 -356
  146. package/node_modules/@noodle-borg/service/dist/routes/business-native-lifecycle.js +46 -0
  147. package/node_modules/@noodle-borg/service/dist/routes/business-workspaces.js +14 -1
  148. package/node_modules/@noodle-borg/service/dist/service.js +1 -0
  149. package/node_modules/@noodle-borg/service/dist/web-capabilities.js +3 -1
  150. package/node_modules/@noodle-borg/service/package.json +2 -1
  151. package/node_modules/@noodle-borg/wire-contracts/dist/application-draft-validation.d.ts +59 -0
  152. package/node_modules/@noodle-borg/wire-contracts/dist/application-draft-validation.js +38 -0
  153. package/node_modules/@noodle-borg/wire-contracts/dist/business-information.d.ts +144 -50
  154. package/node_modules/@noodle-borg/wire-contracts/dist/business-information.js +44 -4
  155. package/node_modules/@noodle-borg/wire-contracts/dist/business-workspaces.d.ts +70 -0
  156. package/node_modules/@noodle-borg/wire-contracts/dist/business-workspaces.js +31 -1
  157. package/node_modules/@noodle-borg/wire-contracts/dist/index.d.ts +2 -0
  158. package/node_modules/@noodle-borg/wire-contracts/dist/index.js +2 -0
  159. package/node_modules/@noodle-borg/wire-contracts/dist/native-record-lifecycle.d.ts +81 -0
  160. package/node_modules/@noodle-borg/wire-contracts/dist/native-record-lifecycle.js +39 -0
  161. package/node_modules/@noodle-borg/wire-contracts/dist/whatsapp.d.ts +26 -0
  162. package/node_modules/@noodle-borg/wire-contracts/dist/whatsapp.js +24 -1
  163. package/node_modules/@noodleseed/assistant/package.json +1 -1
  164. package/package.json +2 -2
@@ -1,15 +1,13 @@
1
1
  import { readJsonBody, sendJson } from '@noodle-borg/transport-http';
2
- import { BusinessGrantCreateRequestSchema, formatWireError, ManagedRecordCreateRequestSchema, ManagedRecordMutationRequestSchema, ManagedRecordStatusSchema, SolutionInstallationCreateRequestSchema, SolutionInstallationIntakeRequestSchema, } from '@noodle-borg/wire-contracts';
3
- import { admitBusinessMutation, admitBusinessTarget, authorizeBusinessApi, } from '../business-api-admission.js';
2
+ import { BusinessGrantCreateRequestSchema, formatWireError, SolutionInstallationCreateRequestSchema, SolutionInstallationIntakeRequestSchema, } from '@noodle-borg/wire-contracts';
3
+ import { admitBusinessTarget } from '../business-api-admission.js';
4
4
  import { resolveInstallDefinition } from '../business-information/definition-resolver.js';
5
- import { BUILT_IN_SOLUTION_PROFILES, businessGrantAllows, MANAGED_SOLUTION_PROFILE_KEYS, PayloadValidationError, validateCollectionEnabled, } from '../business-information/portable.js';
5
+ import { BUILT_IN_SOLUTION_PROFILES, MANAGED_SOLUTION_PROFILE_KEYS, validateCollectionEnabled, } from '../business-information/portable.js';
6
6
  import { sendForbidden } from '../http-util.js';
7
- import { loadExternalRecord, sendExternalRecordPage, sourceBindingKey, unsupportedExternalOperation, } from './business-information-external.js';
7
+ import { requireIdentity, requireInstallationGrant, requireInstallationPermission, resolveBusinessStaffGrant, runBusinessStaffOperation, } from './business-information-access.js';
8
8
  import { installationProjection, retryInstallationActivation, stableInstallationId, } from './business-information-installation.js';
9
- import { createNativeRecord } from './business-information-record-create.js';
10
- import { cursorRequest, sendNativeRecordActivity } from './business-information-record-reads.js';
11
- import { expectedRevision, mutationPermission, parseNativeRecordQuery, parseBusinessPaging as parsePaging, requestIdempotencyKey, } from './business-information-request.js';
12
- import { collectionToWire, externalRecordToWire, grantToWire, profileToWire, recordDetailToWire, recordToWire, } from './business-information-wire.js';
9
+ import { expectedRevision } from './business-information-request.js';
10
+ import { grantToWire, profileToWire } from './business-information-wire.js';
13
11
  import { canManageMembers } from './org-admin.js';
14
12
  export function handleSolutionCatalog(req, res) {
15
13
  if (req.method !== 'GET') {
@@ -70,7 +68,7 @@ export async function handleSolutionInstallations(req, res, ref, deps) {
70
68
  code: 'installation_conflict',
71
69
  });
72
70
  }
73
- const grant = await deps.store.getGrant(result.installation.scope, identity.subject);
71
+ const grant = await resolveBusinessStaffGrant(deps, result.installation.scope, identity.subject);
74
72
  if (grant === undefined || grant.revokedAt !== undefined) {
75
73
  return sendForbidden(res, 'business grant required');
76
74
  }
@@ -107,7 +105,7 @@ export async function handleSolutionInstallations(req, res, ref, deps) {
107
105
  const installations = await deps.store.listInstallations(ref.org);
108
106
  const permitted = [];
109
107
  for (const installation of installations) {
110
- const grant = await deps.store.getGrant(installation.scope, identity.subject);
108
+ const grant = await resolveBusinessStaffGrant(deps, installation.scope, identity.subject);
111
109
  if (grant !== undefined && grant.revokedAt === undefined) {
112
110
  permitted.push({ installation, role: grant.role });
113
111
  }
@@ -142,12 +140,12 @@ export async function handleSolutionInstallations(req, res, ref, deps) {
142
140
  const parsed = SolutionInstallationIntakeRequestSchema.safeParse(body.value);
143
141
  if (!parsed.success)
144
142
  return invalid(res, parsed.error);
145
- const result = await deps.store.setIntakeState({
143
+ const result = await runBusinessStaffOperation(deps, authorized.scope, identity.subject, 'installation:administer', () => deps.store.setIntakeState({
146
144
  scope: authorized.scope,
147
145
  expectedRevision: parsed.data.expectedRevision,
148
146
  active: parsed.data.active,
149
147
  actorSubject: identity.subject,
150
- });
148
+ }));
151
149
  if (!result.ok)
152
150
  return mutationFailure(res, result);
153
151
  return sendJson(res, 200, {
@@ -213,350 +211,8 @@ export async function handleBusinessGrants(req, res, ref, deps) {
213
211
  }
214
212
  return methodNotAllowed(res);
215
213
  }
216
- export async function handleManagedRecords(req, res, url, ref, deps) {
217
- const identity = await requireIdentity(req, res, deps);
218
- if (identity === false)
219
- return;
220
- if (ref.collection === undefined)
221
- return sendJson(res, 404, { error: 'not found' });
222
- const collection = ref.collection;
223
- if (ref.action === 'activity' && req.method === 'GET' && ref.recordId !== undefined) {
224
- const authorized = await requireInstallationPermission(res, ref, identity, 'records:read', deps);
225
- if (authorized === undefined)
226
- return;
227
- const definition = enabledCollection(res, authorized.installation, collection);
228
- if (definition === undefined)
229
- return;
230
- if (definition.authority.authority === 'external') {
231
- return sendJson(res, 200, { ok: true, data: { activities: [] } });
232
- }
233
- return sendNativeRecordActivity(res, url, authorized.installation.scope, collection, ref.recordId, deps.store);
234
- }
235
- if (ref.action === 'export' && req.method === 'GET') {
236
- const authorized = await requireInstallationPermission(res, ref, identity, 'records:export', deps);
237
- if (authorized === undefined)
238
- return;
239
- const definition = enabledCollection(res, authorized.installation, collection);
240
- if (definition === undefined)
241
- return;
242
- const paging = parsePaging(url, 500);
243
- if (!paging.ok)
244
- return sendJson(res, 400, { error: paging.error });
245
- if (definition.authority.authority === 'external') {
246
- return sendExternalRecordPage(res, authorized.installation, definition, { ...paging.value, limit: paging.value.limit ?? 500 }, deps);
247
- }
248
- const page = await cursorRequest(res, () => deps.store.exportRequests({
249
- scope: authorized.installation.scope,
250
- collectionKey: collection,
251
- ...paging.value,
252
- includeDeleted: url.searchParams.get('includeDeleted') === 'true',
253
- }));
254
- if (page === undefined)
255
- return;
256
- return sendJson(res, 200, {
257
- ok: true,
258
- data: {
259
- records: page.records.map((record) => ({
260
- ...recordToWire(record),
261
- ...(record.content ? { notes: record.content.notes } : {}),
262
- })),
263
- ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }),
264
- },
265
- });
266
- }
267
- if (ref.recordId === undefined && req.method === 'GET') {
268
- const authorized = await requireInstallationPermission(res, ref, identity, 'records:read', deps);
269
- if (authorized === undefined)
270
- return;
271
- const definition = enabledCollection(res, authorized.installation, collection);
272
- if (definition === undefined)
273
- return;
274
- const paging = parsePaging(url, 100);
275
- if (!paging.ok)
276
- return sendJson(res, 400, { error: paging.error });
277
- const query = parseNativeRecordQuery(url);
278
- if (!query.ok)
279
- return sendJson(res, 400, { error: query.error, code: 'invalid_query' });
280
- if (definition.authority.authority === 'external') {
281
- if (Object.keys(query.value).length > 0)
282
- return sendJson(res, 400, {
283
- error: 'Field and date queries currently require a native collection.',
284
- code: 'invalid_query',
285
- });
286
- return sendExternalRecordPage(res, authorized.installation, definition, paging.value, deps);
287
- }
288
- const statusValue = url.searchParams.get('status');
289
- const status = statusValue === null ? undefined : ManagedRecordStatusSchema.safeParse(statusValue);
290
- if (status !== undefined && !status.success)
291
- return sendJson(res, 400, { error: 'invalid status' });
292
- const assigneeSubject = url.searchParams.get('assigneeSubject') ?? undefined;
293
- const page = await cursorRequest(res, () => deps.store.listRequests({
294
- scope: authorized.installation.scope,
295
- collectionKey: collection,
296
- ...paging.value,
297
- ...query.value,
298
- ...(status === undefined ? {} : { status: status.data }),
299
- ...(assigneeSubject === undefined ? {} : { assigneeSubject }),
300
- includeDeleted: url.searchParams.get('includeDeleted') === 'true',
301
- }));
302
- if (page === undefined)
303
- return;
304
- return sendJson(res, 200, {
305
- ok: true,
306
- data: {
307
- records: page.records.map(recordToWire),
308
- ...(page.nextCursor === undefined ? {} : { nextCursor: page.nextCursor }),
309
- },
310
- });
311
- }
312
- if (ref.recordId === undefined && req.method === 'POST') {
313
- const authorized = await requireInstallationPermission(res, ref, identity, 'records:create', deps);
314
- if (authorized === undefined)
315
- return;
316
- const definition = enabledCollection(res, authorized.installation, collection);
317
- if (definition === undefined)
318
- return;
319
- if (definition.authority.authority === 'external') {
320
- return unsupportedExternalOperation(res, 'create');
321
- }
322
- const idempotencyKey = requestIdempotencyKey(req);
323
- if (idempotencyKey === undefined) {
324
- return sendJson(res, 400, { error: 'Idempotency-Key header is required (1-128 characters)' });
325
- }
326
- const body = await readJsonBody(req, deps.maxBody);
327
- if (!body.ok)
328
- return sendJson(res, body.status, { error: body.error });
329
- const parsed = ManagedRecordCreateRequestSchema.safeParse(body.value);
330
- if (!parsed.success)
331
- return invalid(res, parsed.error);
332
- const result = await createNativeRecord(res, deps, {
333
- installation: authorized.installation,
334
- collection,
335
- idempotencyKey,
336
- payload: parsed.data.payload,
337
- origin: { kind: 'portal' },
338
- actorSubject: identity.subject,
339
- admit: () => admitBusinessMutation(res, authorized.scope),
340
- });
341
- if (result === undefined)
342
- return;
343
- if (result.disposition === 'conflict')
344
- return idempotencyConflict(res);
345
- if (result.disposition === 'paused') {
346
- return sendJson(res, 503, {
347
- error: 'Record creation requires an active application.',
348
- code: 'application_unavailable',
349
- });
350
- }
351
- return sendJson(res, result.disposition === 'created' ? 201 : 200, {
352
- ok: true,
353
- data: { record: recordToWire(result.record) },
354
- });
355
- }
356
- if (ref.recordId !== undefined && req.method === 'GET') {
357
- const authorized = await requireInstallationPermission(res, ref, identity, 'records:read', deps);
358
- if (authorized === undefined)
359
- return;
360
- const definition = enabledCollection(res, authorized.installation, collection);
361
- if (definition === undefined)
362
- return;
363
- if (definition.authority.authority === 'external') {
364
- const loaded = await loadExternalRecord(authorized.installation, definition, ref.recordId, deps);
365
- if (loaded === undefined)
366
- return sendJson(res, 503, { error: 'collection source unavailable' });
367
- if (loaded.record === undefined)
368
- return sendJson(res, 404, { error: 'record not found' });
369
- return sendJson(res, 200, {
370
- ok: true,
371
- data: {
372
- record: externalRecordToWire(loaded.record, loaded.binding),
373
- collection: collectionToWire(definition),
374
- },
375
- });
376
- }
377
- const record = await deps.store.getRequest(authorized.installation.scope, collection, ref.recordId);
378
- if (record === undefined)
379
- return sendJson(res, 404, { error: 'record not found' });
380
- return sendJson(res, 200, {
381
- ok: true,
382
- data: recordDetailToWire(authorized.installation, record),
383
- });
384
- }
385
- if (ref.recordId !== undefined && req.method === 'PATCH') {
386
- const body = await readJsonBody(req, deps.maxBody);
387
- if (!body.ok)
388
- return sendJson(res, body.status, { error: body.error });
389
- const parsed = ManagedRecordMutationRequestSchema.safeParse(body.value);
390
- if (!parsed.success)
391
- return invalid(res, parsed.error);
392
- const permission = mutationPermission(parsed.data.operation);
393
- const authorized = await requireInstallationPermission(res, ref, identity, permission, deps);
394
- if (authorized === undefined)
395
- return;
396
- const definition = enabledCollection(res, authorized.installation, collection);
397
- if (definition === undefined)
398
- return;
399
- if (definition.authority.authority === 'external') {
400
- return unsupportedExternalOperation(res, parsed.data.operation);
401
- }
402
- if (parsed.data.operation === 'migrate-schema') {
403
- const result = await deps.store.migrateLegacyRequest({
404
- scope: authorized.installation.scope,
405
- collectionKey: collection,
406
- id: ref.recordId,
407
- expectedRevision: parsed.data.expectedRevision,
408
- actorSubject: identity.subject,
409
- });
410
- if (!result.ok)
411
- return mutationFailure(res, result);
412
- return sendJson(res, 200, { ok: true, data: { record: recordToWire(result.record) } });
413
- }
414
- let operation;
415
- if (parsed.data.operation === 'update') {
416
- operation = {
417
- kind: 'update',
418
- payload: parsed.data.patch,
419
- ...(parsed.data.unset ? { unset: parsed.data.unset } : {}),
420
- };
421
- }
422
- else if (parsed.data.operation === 'assign') {
423
- if (parsed.data.assigneeSubject !== null) {
424
- const assigneeGrant = await deps.store.getGrant(authorized.installation.scope, parsed.data.assigneeSubject);
425
- if (assigneeGrant === undefined ||
426
- assigneeGrant.revokedAt !== undefined ||
427
- assigneeGrant.role === 'viewer') {
428
- return sendJson(res, 422, {
429
- error: 'assignee must have an active operator grant',
430
- code: 'invalid_assignee',
431
- });
432
- }
433
- }
434
- operation = {
435
- kind: 'assign',
436
- assigneeSubject: parsed.data.assigneeSubject ?? undefined,
437
- };
438
- }
439
- else if (parsed.data.operation === 'set-status') {
440
- operation = { kind: 'set_status', status: parsed.data.status };
441
- }
442
- else {
443
- operation = { kind: 'add_note', note: parsed.data.note };
444
- }
445
- let result;
446
- try {
447
- result = await deps.store.mutateRequest({
448
- scope: authorized.installation.scope,
449
- collectionKey: collection,
450
- id: ref.recordId,
451
- expectedRevision: parsed.data.expectedRevision,
452
- actorSubject: identity.subject,
453
- operation,
454
- });
455
- }
456
- catch (error) {
457
- if (error instanceof PayloadValidationError) {
458
- return sendJson(res, 400, { error: error.message, code: error.code });
459
- }
460
- throw error;
461
- }
462
- if (!result.ok)
463
- return mutationFailure(res, result);
464
- return sendJson(res, 200, { ok: true, data: { record: recordToWire(result.record) } });
465
- }
466
- if (ref.recordId !== undefined && req.method === 'DELETE') {
467
- const authorized = await requireInstallationPermission(res, ref, identity, 'records:delete', deps);
468
- if (authorized === undefined)
469
- return;
470
- const definition = enabledCollection(res, authorized.installation, collection);
471
- if (definition === undefined)
472
- return;
473
- const body = await readJsonBody(req, deps.maxBody);
474
- if (!body.ok)
475
- return sendJson(res, body.status, { error: body.error });
476
- const revision = expectedRevision(body.value);
477
- if (revision === undefined) {
478
- return sendJson(res, 400, { error: 'expectedRevision must be a positive integer' });
479
- }
480
- if (definition.authority.authority === 'external') {
481
- const loaded = await loadExternalRecord(authorized.installation, definition, ref.recordId, deps);
482
- if (loaded === undefined)
483
- return sendJson(res, 503, { error: 'collection source unavailable' });
484
- if (loaded.record === undefined)
485
- return sendJson(res, 404, { error: 'record not found' });
486
- const { record } = loaded;
487
- if (record.revision !== revision) {
488
- return sendJson(res, 409, {
489
- error: 'record revision conflict',
490
- code: 'revision_conflict',
491
- currentRevision: record.revision,
492
- });
493
- }
494
- await deps.sourceStore?.suppressExternalRecord({
495
- ...sourceBindingKey(authorized.installation, definition),
496
- sourceId: record.source.id,
497
- reason: 'customer_request',
498
- now: now(deps),
499
- });
500
- const deletedAt = now(deps).toISOString();
501
- return sendJson(res, 200, {
502
- ok: true,
503
- data: {
504
- recordId: record.id,
505
- authority: 'external',
506
- disposition: 'suppressed',
507
- deletedAt,
508
- },
509
- });
510
- }
511
- const result = await deps.store.deleteRequest({
512
- scope: authorized.installation.scope,
513
- collectionKey: collection,
514
- id: ref.recordId,
515
- expectedRevision: revision,
516
- actorSubject: identity.subject,
517
- reason: 'customer_request',
518
- });
519
- if (!result.ok)
520
- return mutationFailure(res, result);
521
- return sendJson(res, 200, {
522
- ok: true,
523
- data: {
524
- recordId: result.record.id,
525
- authority: 'native',
526
- disposition: 'deleted',
527
- deletedAt: result.record.deletedAt,
528
- },
529
- });
530
- }
531
- return methodNotAllowed(res);
532
- }
533
- export async function requireIdentity(req, res, deps) {
534
- return authorizeBusinessApi(req, res, deps);
535
- }
536
- export async function requireInstallationGrant(res, ref, identity, deps, permission) {
537
- if (ref.installationId === undefined)
538
- return undefined;
539
- const installation = await deps.store.getInstallationById(ref.org, ref.installationId);
540
- if (installation === undefined) {
541
- sendJson(res, 404, { error: 'installation not found' });
542
- return undefined;
543
- }
544
- const grant = await deps.store.getGrant(installation.scope, identity.subject);
545
- if (grant?.revokedAt !== undefined || grant === undefined) {
546
- sendForbidden(res, 'business grant required');
547
- return undefined;
548
- }
549
- if (permission && !businessGrantAllows(grant, permission)) {
550
- sendForbidden(res, `business permission required: ${permission}`);
551
- return undefined;
552
- }
553
- if (!(await admitBusinessTarget(res, installation.scope)))
554
- return undefined;
555
- return { installation, scope: installation.scope, grant };
556
- }
557
- export async function requireInstallationPermission(res, ref, identity, permission, deps) {
558
- return requireInstallationGrant(res, ref, identity, deps, permission);
559
- }
214
+ export { requireIdentity, requireInstallationGrant, requireInstallationPermission, } from './business-information-access.js';
215
+ export { handleManagedRecords } from './business-information-records.js';
560
216
  export function mutationFailure(res, result) {
561
217
  const status = result.reason === 'not_found'
562
218
  ? 404
@@ -0,0 +1,46 @@
1
+ import { readJsonBody, sendJson } from '@noodle-borg/transport-http';
2
+ import { NativeRecordLifecycleMigrateRequestSchema, NativeRecordLifecyclePreviewResponseSchema, NativeRecordLifecycleResponseSchema, } from '@noodle-borg/wire-contracts';
3
+ import { NativeLifecycleError } from '../business-information/native-record-lifecycle.js';
4
+ import { invalid, methodNotAllowed, requireIdentity, requireInstallationPermission, } from './business-information.js';
5
+ export async function handleNativeRecordLifecycle(req, res, ref, deps) {
6
+ res.setHeader('cache-control', 'private, no-store');
7
+ if (req.method !== 'GET' && req.method !== 'POST')
8
+ return methodNotAllowed(res);
9
+ const identity = await requireIdentity(req, res, deps);
10
+ if (identity === false)
11
+ return;
12
+ const authorized = await requireInstallationPermission(res, ref, identity, 'installation:administer', deps);
13
+ if (!authorized)
14
+ return;
15
+ try {
16
+ if (req.method === 'GET') {
17
+ const data = await deps.store.nativeLifecycle.preview(authorized.scope, identity.subject);
18
+ return sendJson(res, 200, NativeRecordLifecyclePreviewResponseSchema.parse({ ok: true, data }));
19
+ }
20
+ const body = await readJsonBody(req, deps.maxBody);
21
+ if (!body.ok)
22
+ return sendJson(res, body.status, { error: body.error });
23
+ const parsed = NativeRecordLifecycleMigrateRequestSchema.safeParse(body.value);
24
+ if (!parsed.success)
25
+ return invalid(res, parsed.error);
26
+ const data = await deps.store.nativeLifecycle.migrate({
27
+ scope: authorized.scope,
28
+ actor: identity.subject,
29
+ preview: parsed.data.preview,
30
+ });
31
+ return sendJson(res, 200, NativeRecordLifecycleResponseSchema.parse({ ok: true, data }));
32
+ }
33
+ catch (error) {
34
+ if (!(error instanceof NativeLifecycleError))
35
+ throw error;
36
+ return sendJson(res, error.code === 'lifecycle_denied' ? 403 : error.code === 'lifecycle_conflict' ? 409 : 503, {
37
+ code: error.code,
38
+ error: error.code === 'lifecycle_conflict'
39
+ ? 'The reviewed inventory changed or expired. Review again before applying the migration.'
40
+ : error.code === 'lifecycle_denied'
41
+ ? 'Current Owner or Admin permission is required.'
42
+ : 'Native record lifecycle is unavailable.',
43
+ });
44
+ }
45
+ }
46
+ //# sourceMappingURL=business-native-lifecycle.js.map
@@ -1,5 +1,5 @@
1
1
  import { readJsonBody, sendJson } from '@noodle-borg/transport-http';
2
- import { BusinessWorkspaceAcceptRequestSchema, BusinessWorkspaceInvitationRequestSchema, BusinessWorkspaceIssuedInvitationResponseSchema, BusinessWorkspaceMutationResponseSchema, BusinessWorkspaceResponseSchema, BusinessWorkspaceRevisionRequestSchema, BusinessWorkspaceRoleChangeRequestSchema, } from '@noodle-borg/wire-contracts';
2
+ import { BusinessWorkspaceAcceptRequestSchema, BusinessWorkspaceInvitationRequestSchema, BusinessWorkspaceIssuedInvitationResponseSchema, BusinessWorkspaceListResponseSchema, BusinessWorkspaceMutationResponseSchema, BusinessWorkspaceResponseSchema, BusinessWorkspaceRevisionRequestSchema, BusinessWorkspaceRoleChangeRequestSchema, } from '@noodle-borg/wire-contracts';
3
3
  import { admitBusinessTarget, authorizeBusinessApi } from '../business-api-admission.js';
4
4
  import { BusinessWorkspaceError } from '../business-workspaces/contracts.js';
5
5
  import { businessWorkspaceMethod, parseBusinessWorkspacePath, } from '../business-workspaces/paths.js';
@@ -30,6 +30,19 @@ export async function handleBusinessWorkspaceRoute(req, res, url, deps) {
30
30
  }
31
31
  }
32
32
  async function handle(req, res, url, deps) {
33
+ if (url.pathname === '/v1/me/business-workspaces') {
34
+ const identity = await authorizeBusinessApi(req, res, deps);
35
+ if (!identity)
36
+ return;
37
+ if (req.method !== 'GET') {
38
+ res.setHeader('allow', 'GET');
39
+ return sendJson(res, 405, { error: 'method not allowed' });
40
+ }
41
+ if (new Set(url.searchParams.keys()).size !== [...url.searchParams].length)
42
+ return invalid(res);
43
+ const data = await deps.workspaces.listForSubject(identity.subject, Object.fromEntries(url.searchParams));
44
+ return sendJson(res, 200, BusinessWorkspaceListResponseSchema.parse({ ok: true, data }));
45
+ }
33
46
  const ref = parseBusinessWorkspacePath(url.pathname);
34
47
  if (!ref)
35
48
  return sendJson(res, 404, { error: 'not found' });
@@ -309,6 +309,7 @@ export function createServiceHandler(registry, options = {}) {
309
309
  pageServiceUrl: options.publicBaseUrl,
310
310
  ...(whatsapp ? { readWhatsApp: whatsapp.projection.bind(whatsapp) } : {}),
311
311
  store: businessInformationStore,
312
+ ...(options.businessAuthoring ? { workspaces: options.businessAuthoring.workspaces } : {}),
312
313
  ...(businessOnboarding ? { businessOnboarding } : {}),
313
314
  activateInstallation,
314
315
  readInstallationActivation,
@@ -30,7 +30,9 @@ export function deploymentWebConnector(artifact, tenant, deploymentId, service)
30
30
  }, call.caller).allow,
31
31
  anonymous: call.caller === undefined || call.caller.identityKind === 'anonymous',
32
32
  ...(call.caller === undefined ? {} : { subject: call.caller.subject }),
33
- ...(call.publicAdmission === undefined ? {} : { network: call.publicAdmission.network }),
33
+ ...(call.publicAdmission?.network === undefined
34
+ ? {}
35
+ : { network: call.publicAdmission.network }),
34
36
  budget: call.capabilityBudget,
35
37
  ...(call.signal === undefined ? {} : { signal: call.signal }),
36
38
  });
@@ -37,10 +37,11 @@
37
37
  "typecheck": "tsc -p tsconfig.json --noEmit"
38
38
  },
39
39
  "dependencies": {
40
+ "@noodle-borg/authoring": "0.0.0",
40
41
  "@noodle-borg/managed-capabilities": "0.0.0",
41
42
  "@modelcontextprotocol/sdk": "^1.29.0",
42
43
  "@noodle-borg/admission-limits": "0.0.0",
43
- "@noodle-borg/agent-kit": "0.106.0",
44
+ "@noodle-borg/agent-kit": "0.107.0",
44
45
  "@noodle-borg/app-package": "0.0.0",
45
46
  "@noodle-borg/assistant-gateway": "0.0.0",
46
47
  "@noodle-borg/auth": "0.0.0",
@@ -0,0 +1,59 @@
1
+ import { z } from 'zod';
2
+ export declare const ApplicationDraftValidationResponseSchema: z.ZodObject<{
3
+ ok: z.ZodLiteral<true>;
4
+ data: z.ZodObject<{
5
+ validation: z.ZodDiscriminatedUnion<[z.ZodObject<{
6
+ status: z.ZodLiteral<"valid">;
7
+ compilerDigest: z.ZodString;
8
+ artifactDigest: z.ZodString;
9
+ issues: z.ZodArray<z.ZodNever>;
10
+ draftId: z.ZodString;
11
+ revision: z.ZodNumber;
12
+ sourceDigest: z.ZodString;
13
+ check: z.ZodLiteral<"source-and-manifest">;
14
+ published: z.ZodLiteral<false>;
15
+ }, z.core.$strict>, z.ZodObject<{
16
+ status: z.ZodLiteral<"invalid">;
17
+ issues: z.ZodArray<z.ZodObject<{
18
+ code: z.ZodString;
19
+ message: z.ZodString;
20
+ path: z.ZodOptional<z.ZodString>;
21
+ }, z.core.$strict>>;
22
+ draftId: z.ZodString;
23
+ revision: z.ZodNumber;
24
+ sourceDigest: z.ZodString;
25
+ check: z.ZodLiteral<"source-and-manifest">;
26
+ published: z.ZodLiteral<false>;
27
+ }, z.core.$strict>], "status">;
28
+ }, z.core.$strict>;
29
+ }, z.core.$strict>;
30
+ export declare const ApplicationDraftValidationClientResponseSchema: z.ZodObject<{
31
+ ok: z.ZodLiteral<true>;
32
+ data: z.ZodObject<{
33
+ validation: z.ZodDiscriminatedUnion<[z.ZodObject<{
34
+ status: z.ZodLiteral<"valid">;
35
+ compilerDigest: z.ZodString;
36
+ artifactDigest: z.ZodString;
37
+ issues: z.ZodArray<z.ZodNever>;
38
+ draftId: z.ZodString;
39
+ revision: z.ZodNumber;
40
+ sourceDigest: z.ZodString;
41
+ check: z.ZodLiteral<"source-and-manifest">;
42
+ published: z.ZodLiteral<false>;
43
+ }, z.core.$strict>, z.ZodObject<{
44
+ status: z.ZodLiteral<"invalid">;
45
+ issues: z.ZodArray<z.ZodObject<{
46
+ code: z.ZodString;
47
+ message: z.ZodString;
48
+ path: z.ZodOptional<z.ZodString>;
49
+ }, z.core.$strict>>;
50
+ draftId: z.ZodString;
51
+ revision: z.ZodNumber;
52
+ sourceDigest: z.ZodString;
53
+ check: z.ZodLiteral<"source-and-manifest">;
54
+ published: z.ZodLiteral<false>;
55
+ }, z.core.$strict>], "status">;
56
+ }, z.core.$strict>;
57
+ }, z.core.$strict>;
58
+ export type ApplicationDraftValidation = z.infer<typeof ApplicationDraftValidationResponseSchema>['data']['validation'];
59
+ //# sourceMappingURL=application-draft-validation.d.ts.map
@@ -0,0 +1,38 @@
1
+ import { z } from 'zod';
2
+ import { ApplicationDraftIdSchema } from './application-drafts.js';
3
+ /** A source check is transient evidence, never a stored publication/activation receipt. */
4
+ function response(additive) {
5
+ const object = additive ? z.object : z.strictObject;
6
+ const common = {
7
+ draftId: ApplicationDraftIdSchema,
8
+ revision: z.number().int().min(1).max(2147483647),
9
+ sourceDigest: z.string().regex(/^[a-f0-9]{64}$/),
10
+ check: z.literal('source-and-manifest'),
11
+ published: z.literal(false),
12
+ };
13
+ const validation = z.discriminatedUnion('status', [
14
+ object({
15
+ ...common,
16
+ status: z.literal('valid'),
17
+ compilerDigest: z.string().regex(/^sha256:[a-f0-9]{64}$/),
18
+ artifactDigest: z.string().regex(/^[a-f0-9]{64}$/),
19
+ issues: z.array(z.never()).max(0),
20
+ }),
21
+ object({
22
+ ...common,
23
+ status: z.literal('invalid'),
24
+ issues: z
25
+ .array(object({
26
+ code: z.string().regex(/^[a-z][a-z0-9_]{0,79}$/),
27
+ message: z.string().max(500),
28
+ path: z.string().max(240).optional(),
29
+ }))
30
+ .min(1)
31
+ .max(20),
32
+ }),
33
+ ]);
34
+ return object({ ok: z.literal(true), data: object({ validation }) });
35
+ }
36
+ export const ApplicationDraftValidationResponseSchema = response(false);
37
+ export const ApplicationDraftValidationClientResponseSchema = response(true);
38
+ //# sourceMappingURL=application-draft-validation.js.map