@noodleseed/one 0.168.0 → 0.170.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 (142) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +3 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/capabilities.d.ts +19 -0
  5. package/dist/commands/capabilities.d.ts.map +1 -0
  6. package/dist/commands/capabilities.js +128 -0
  7. package/dist/commands/capabilities.js.map +1 -0
  8. package/dist/commands/deployment-delete-ops.d.ts +12 -0
  9. package/dist/commands/deployment-delete-ops.d.ts.map +1 -0
  10. package/dist/commands/deployment-delete-ops.js +230 -0
  11. package/dist/commands/deployment-delete-ops.js.map +1 -0
  12. package/dist/commands/deployment-delete-recovery.d.ts +35 -0
  13. package/dist/commands/deployment-delete-recovery.d.ts.map +1 -0
  14. package/dist/commands/deployment-delete-recovery.js +99 -0
  15. package/dist/commands/deployment-delete-recovery.js.map +1 -0
  16. package/dist/commands/deployments-ops.d.ts +2 -1
  17. package/dist/commands/deployments-ops.d.ts.map +1 -1
  18. package/dist/commands/deployments-ops.js +5 -1
  19. package/dist/commands/deployments-ops.js.map +1 -1
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +1 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/plugin-mode/plugin-operation-contract.d.ts +2 -2
  25. package/node_modules/@noodle-borg/agent-kit/dist/curated/command-groups.js +4 -0
  26. package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +1 -1
  27. package/node_modules/@noodle-borg/agent-kit/dist/generated/surface.js +2 -0
  28. package/node_modules/@noodle-borg/agent-kit/dist/skill-authoring-refs.js +3 -2
  29. package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
  30. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-interactive.d.ts +1 -0
  31. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-interactive.js +4 -1
  32. package/node_modules/@noodle-borg/authoring/dist/assistant.d.ts +2 -2
  33. package/node_modules/@noodle-borg/authoring/dist/assistant.js +3 -1
  34. package/node_modules/@noodle-borg/authoring/dist/index.d.ts +1 -0
  35. package/node_modules/@noodle-borg/authoring/dist/index.js +1 -0
  36. package/node_modules/@noodle-borg/authoring/dist/load-authored.js +1 -1
  37. package/node_modules/@noodle-borg/authoring/dist/recording.d.ts +2 -1
  38. package/node_modules/@noodle-borg/authoring/dist/recording.js +29 -1
  39. package/node_modules/@noodle-borg/authoring/dist/server.d.ts +3 -0
  40. package/node_modules/@noodle-borg/authoring/dist/server.js +5 -1
  41. package/node_modules/@noodle-borg/authoring/dist/web-capabilities.d.ts +17 -0
  42. package/node_modules/@noodle-borg/authoring/dist/web-capabilities.js +15 -0
  43. package/node_modules/@noodle-borg/authoring/package.json +1 -0
  44. package/node_modules/@noodle-borg/capabilities/dist/index.js +7 -0
  45. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-hosted-observability.js +102 -0
  46. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-tenant-resources.js +27 -1
  47. package/node_modules/@noodle-borg/compiler/dist/artifact/assemble.js +3 -0
  48. package/node_modules/@noodle-borg/compiler/dist/artifact/types.d.ts +2 -0
  49. package/node_modules/@noodle-borg/compiler/dist/artifact/version.d.ts +2 -1
  50. package/node_modules/@noodle-borg/compiler/dist/artifact/version.js +2 -1
  51. package/node_modules/@noodle-borg/compiler/dist/compile.js +2 -0
  52. package/node_modules/@noodle-borg/compiler/dist/fulfilment-emit.js +18 -3
  53. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +34 -0
  54. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +9 -2
  55. package/node_modules/@noodle-borg/compiler/dist/web-capabilities.js +55 -0
  56. package/node_modules/@noodle-borg/compiler/package.json +1 -0
  57. package/node_modules/@noodle-borg/connector-http/dist/ssrf.js +5 -0
  58. package/node_modules/@noodle-borg/control-plane/dist/delete-deployment.d.ts +44 -0
  59. package/node_modules/@noodle-borg/control-plane/dist/delete-deployment.js +105 -0
  60. package/node_modules/@noodle-borg/control-plane/dist/deployment-deletion-contracts.d.ts +20 -0
  61. package/node_modules/@noodle-borg/control-plane/dist/portable.d.ts +2 -0
  62. package/node_modules/@noodle-borg/control-plane/dist/portable.js +1 -0
  63. package/node_modules/@noodle-borg/knowledge-crawl/dist/bounded-body.js +38 -0
  64. package/node_modules/@noodle-borg/knowledge-crawl/dist/first-party.js +2 -3
  65. package/node_modules/@noodle-borg/knowledge-crawl/dist/html-text.js +10 -2
  66. package/node_modules/@noodle-borg/knowledge-crawl/dist/index.d.ts +1 -0
  67. package/node_modules/@noodle-borg/knowledge-crawl/dist/index.js +1 -0
  68. package/node_modules/@noodle-borg/knowledge-crawl/dist/page-reader.d.ts +26 -0
  69. package/node_modules/@noodle-borg/knowledge-crawl/dist/page-reader.js +127 -0
  70. package/node_modules/@noodle-borg/knowledge-crawl/dist/robots.js +48 -23
  71. package/node_modules/@noodle-borg/knowledge-crawl/package.json +1 -0
  72. package/node_modules/@noodle-borg/managed-capabilities/dist/budget.d.ts +29 -0
  73. package/node_modules/@noodle-borg/managed-capabilities/dist/budget.js +124 -0
  74. package/node_modules/@noodle-borg/managed-capabilities/dist/contracts.d.ts +212 -0
  75. package/node_modules/@noodle-borg/managed-capabilities/dist/contracts.js +113 -0
  76. package/node_modules/@noodle-borg/managed-capabilities/dist/errors.d.ts +8 -0
  77. package/node_modules/@noodle-borg/managed-capabilities/dist/errors.js +21 -0
  78. package/node_modules/@noodle-borg/managed-capabilities/dist/executor.d.ts +26 -0
  79. package/node_modules/@noodle-borg/managed-capabilities/dist/executor.js +156 -0
  80. package/node_modules/@noodle-borg/managed-capabilities/dist/limits.d.ts +15 -0
  81. package/node_modules/@noodle-borg/managed-capabilities/dist/limits.js +15 -0
  82. package/node_modules/@noodle-borg/managed-capabilities/dist/operator-contracts.d.ts +159 -0
  83. package/node_modules/@noodle-borg/managed-capabilities/dist/operator-contracts.js +69 -0
  84. package/node_modules/@noodle-borg/managed-capabilities/dist/operator-route.d.ts +5 -0
  85. package/node_modules/@noodle-borg/managed-capabilities/dist/operator-route.js +160 -0
  86. package/node_modules/@noodle-borg/managed-capabilities/dist/policy-store.d.ts +34 -0
  87. package/node_modules/@noodle-borg/managed-capabilities/dist/policy-store.js +61 -0
  88. package/node_modules/@noodle-borg/managed-capabilities/dist/portable.d.ts +10 -0
  89. package/node_modules/@noodle-borg/managed-capabilities/dist/portable.js +10 -0
  90. package/node_modules/@noodle-borg/managed-capabilities/dist/service.d.ts +83 -0
  91. package/node_modules/@noodle-borg/managed-capabilities/dist/service.js +139 -0
  92. package/node_modules/@noodle-borg/managed-capabilities/dist/urls.d.ts +3 -0
  93. package/node_modules/@noodle-borg/managed-capabilities/dist/urls.js +30 -0
  94. package/node_modules/@noodle-borg/managed-capabilities/package.json +49 -0
  95. package/node_modules/@noodle-borg/module/dist/contract.d.ts +11 -0
  96. package/node_modules/@noodle-borg/runtime/dist/confirmation.js +29 -0
  97. package/node_modules/@noodle-borg/runtime/dist/connector/types.d.ts +2 -0
  98. package/node_modules/@noodle-borg/runtime/dist/execute.d.ts +3 -0
  99. package/node_modules/@noodle-borg/runtime/dist/execute.js +6 -1
  100. package/node_modules/@noodle-borg/runtime/dist/index.d.ts +2 -1
  101. package/node_modules/@noodle-borg/runtime/dist/index.js +2 -1
  102. package/node_modules/@noodle-borg/runtime/dist/interactive.js +10 -1
  103. package/node_modules/@noodle-borg/runtime/dist/operation-execution.js +3 -0
  104. package/node_modules/@noodle-borg/runtime/dist/result-meta.d.ts +2 -0
  105. package/node_modules/@noodle-borg/runtime/dist/result-meta.js +6 -0
  106. package/node_modules/@noodle-borg/runtime/dist/result.d.ts +4 -0
  107. package/node_modules/@noodle-borg/runtime/dist/web-capabilities.d.ts +71 -0
  108. package/node_modules/@noodle-borg/runtime/dist/web-capabilities.js +41 -0
  109. package/node_modules/@noodle-borg/runtime/package.json +1 -0
  110. package/node_modules/@noodle-borg/service/dist/deployment-deletion.js +40 -0
  111. package/node_modules/@noodle-borg/service/dist/local-options.d.ts +1 -0
  112. package/node_modules/@noodle-borg/service/dist/local-web-capabilities.js +13 -0
  113. package/node_modules/@noodle-borg/service/dist/mcp-protocol-runtime.js +21 -0
  114. package/node_modules/@noodle-borg/service/dist/modules/route-dispatch.js +13 -0
  115. package/node_modules/@noodle-borg/service/dist/registry-compile.js +9 -0
  116. package/node_modules/@noodle-borg/service/dist/registry-deletion.js +75 -0
  117. package/node_modules/@noodle-borg/service/dist/registry-state.js +4 -2
  118. package/node_modules/@noodle-borg/service/dist/registry-targets.js +4 -0
  119. package/node_modules/@noodle-borg/service/dist/registry-types.d.ts +1 -0
  120. package/node_modules/@noodle-borg/service/dist/registry.d.ts +2 -0
  121. package/node_modules/@noodle-borg/service/dist/registry.js +10 -23
  122. package/node_modules/@noodle-borg/service/dist/routes/assistant-agent.js +5 -2
  123. package/node_modules/@noodle-borg/service/dist/routes/assistant-interactions.js +7 -4
  124. package/node_modules/@noodle-borg/service/dist/routes/deploy-dispatch.js +15 -1
  125. package/node_modules/@noodle-borg/service/dist/routes/deployment-delete.js +53 -0
  126. package/node_modules/@noodle-borg/service/dist/serve-local.js +14 -0
  127. package/node_modules/@noodle-borg/service/dist/serve-resource-auth.js +10 -0
  128. package/node_modules/@noodle-borg/service/dist/service.js +7 -1
  129. package/node_modules/@noodle-borg/service/dist/store/in-memory.d.ts +2 -1
  130. package/node_modules/@noodle-borg/service/dist/store/in-memory.js +8 -0
  131. package/node_modules/@noodle-borg/service/dist/store/json-file-deletion.js +64 -0
  132. package/node_modules/@noodle-borg/service/dist/store/json-file.d.ts +2 -1
  133. package/node_modules/@noodle-borg/service/dist/store/json-file.js +23 -5
  134. package/node_modules/@noodle-borg/service/dist/store.d.ts +5 -1
  135. package/node_modules/@noodle-borg/service/dist/web-capabilities.js +39 -0
  136. package/node_modules/@noodle-borg/service/package.json +3 -1
  137. package/node_modules/@noodle-borg/wire-contracts/dist/deployment-deletion.d.ts +29 -0
  138. package/node_modules/@noodle-borg/wire-contracts/dist/deployment-deletion.js +33 -0
  139. package/node_modules/@noodle-borg/wire-contracts/dist/index.d.ts +2 -0
  140. package/node_modules/@noodle-borg/wire-contracts/dist/index.js +2 -0
  141. package/node_modules/@noodle-borg/wire-contracts/package.json +1 -0
  142. package/package.json +3 -2
@@ -6,6 +6,7 @@ import type { LocalDevtoolsDelegatedExchangeRuntime } from './local-devtools-del
6
6
  import type { ServerRegistry } from './registry.js';
7
7
  import type { ConfigStore, TenantBridgeAuthConfig } from './store.js';
8
8
  type LocalRuntimeOptions = {
9
+ readonly capabilities?: import('@noodle-borg/managed-capabilities').CapabilityService;
9
10
  readonly assistantStore?: import('@noodle-borg/assistant-gateway/portable').AssistantStore;
10
11
  readonly assistantAppearance?: import('@noodle-borg/assistant-gateway/portable').AssistantAppearanceSettingsStore;
11
12
  readonly publicEmbeds?: import('@noodle-borg/assistant-gateway/portable').PublicEmbedStore;
@@ -0,0 +1,13 @@
1
+ import { InMemoryDailyCounterStore } from '@noodle-borg/admission-limits/portable';
2
+ import { PublicPageReader } from '@noodle-borg/knowledge-crawl';
3
+ import { CapabilityService, InMemoryCapabilityPolicyStore, } from '@noodle-borg/managed-capabilities';
4
+ /** Loopback-only composition. Keep PostgreSQL imports out of the published CLI graph. */
5
+ export function localWebCapabilities() {
6
+ return new CapabilityService({
7
+ profile: 'development',
8
+ policies: new InMemoryCapabilityPolicyStore(),
9
+ counters: new InMemoryDailyCounterStore(),
10
+ reader: new PublicPageReader(),
11
+ });
12
+ }
13
+ //# sourceMappingURL=local-web-capabilities.js.map
@@ -0,0 +1,21 @@
1
+ import { RequestStateManager, requestStateSecretBox, } from '@noodle-borg/protocol';
2
+ /**
3
+ * Build the fleet-shared request-state manager. Static-key deployments use an HKDF domain-separated key;
4
+ * KMS deployments reuse the configured envelope-encryption custodian because its raw KEK cannot be
5
+ * exported for HKDF.
6
+ */
7
+ export function createHostedMcpRequestStateManager(input) {
8
+ if (input.secretMasterKey !== undefined) {
9
+ return new RequestStateManager(requestStateSecretBox(Buffer.from(input.secretMasterKey, 'base64')));
10
+ }
11
+ return input.secretBox === undefined ? undefined : new RequestStateManager(input.secretBox);
12
+ }
13
+ /** Resolve the temporary origin-wide rollout gate from operator configuration. */
14
+ export function resolveMcpProtocolMode(value) {
15
+ if (value === undefined || value === 'dual')
16
+ return 'dual';
17
+ if (value === 'legacy-only')
18
+ return value;
19
+ throw new Error('NOODLE_MCP_PROTOCOL_MODE must be dual or legacy-only');
20
+ }
21
+ //# sourceMappingURL=mcp-protocol-runtime.js.map
@@ -14,6 +14,19 @@ export function createModuleRouteContext(hostRequest, deps) {
14
14
  logger: deps.logger ?? noopModuleLogger,
15
15
  controlPlane: deps.gate,
16
16
  audit: deps.audit,
17
+ capabilityDeployments: {
18
+ get: async (request, scope) => {
19
+ if (request !== hostRequest || !authorizedOrgs.has(scope.org))
20
+ return undefined;
21
+ const target = await deps.registry.getActiveByTenant(scope);
22
+ if (target?.deploymentId === undefined)
23
+ return undefined;
24
+ return {
25
+ deploymentId: target.deploymentId,
26
+ declarations: target.served.artifact.server.capabilities ?? [],
27
+ };
28
+ },
29
+ },
17
30
  platformIdentityRecovery: {
18
31
  reconcile: (plan) => deps.registry.reconcilePlatformAccountReset(plan),
19
32
  customerAuthRestoreProjections: async (deploymentIds) => (await deps.registry.customerAuthRestoreProjections(deploymentIds)).map((projection) => ({
@@ -9,10 +9,12 @@ import { ManagedConfigBroker } from './credential-broker.js';
9
9
  import { deploymentCredentialBrokerOptions } from './credential-broker-options.js';
10
10
  import { deploymentRecordVersionError } from './deployment-record-version.js';
11
11
  import { normalizePersistedManifestForCompile } from './manifest-normalize.js';
12
+ import { registryRecordStillExists } from './registry-deletion.js';
12
13
  import { missingCapabilityErrors, missingSecretErrors, missingVariableErrors, } from './registry-helpers.js';
13
14
  import { servedTargetFor } from './registry-targets.js';
14
15
  import { createDeploymentStateConnector, } from './state-connector-factory.js';
15
16
  import { resolveConfigScope, } from './store.js';
17
+ import { deploymentWebConnector } from './web-capabilities.js';
16
18
  /** Reject unsupported persisted semantics before constructing executable runtime dependencies. */
17
19
  export async function compilePersistedRegistryRecord(record, compileTarget) {
18
20
  const versionError = deploymentRecordVersionError(record);
@@ -196,11 +198,13 @@ export async function compileRegistryTarget(context, input) {
196
198
  artifact,
197
199
  ...(boundDeploymentId === undefined ? {} : { deploymentId: boundDeploymentId }),
198
200
  });
201
+ const webConnector = deploymentWebConnector(artifact, tenant, boundDeploymentId, context.capabilities);
199
202
  return {
200
203
  artifact,
201
204
  deps: {
202
205
  connectors: new InMemoryConnectorRegistry([
203
206
  ...context.platformConnectors,
207
+ ...(webConnector === undefined ? [] : [webConnector]),
204
208
  ...(nativeRecords === undefined ? [] : [nativeRecords]),
205
209
  ...(stateConnector !== undefined ? [stateConnector] : []),
206
210
  ...httpConnectors,
@@ -252,6 +256,11 @@ export async function loadPersistedRegistryTarget(source, context) {
252
256
  if (record.active &&
253
257
  (await context.hasCustomerAuthConflict(record, built.served.artifact.server.auth)))
254
258
  return undefined;
259
+ const exists = state.store === undefined
260
+ ? state.records.has(deploymentId)
261
+ : await registryRecordStillExists(state, deploymentId);
262
+ if (!exists)
263
+ return undefined;
255
264
  const target = servedTargetFor(record, built.served, context.customerVerifierFactory);
256
265
  state.servers.set(deploymentId, target);
257
266
  state.records.set(deploymentId, record);
@@ -0,0 +1,75 @@
1
+ import { planDeploymentDeletion } from './deployment-deletion.js';
2
+ import { targetForPersistedRecord } from './registry-targets.js';
3
+ import { validateTenantRef } from './store/validate.js';
4
+ export function evictDeletedDeployments(state, ids) {
5
+ for (const id of ids) {
6
+ state.records.delete(id);
7
+ state.servers.delete(id);
8
+ }
9
+ for (const [key, id] of state.activeTenants)
10
+ if (ids.has(id))
11
+ state.activeTenants.delete(key);
12
+ }
13
+ export async function deleteRegistryDeployments(state, ref, selection) {
14
+ const safe = validateTenantRef(ref);
15
+ const result = state.store === undefined
16
+ ? planDeploymentDeletion([...state.records.values()], safe, selection)
17
+ : await state.store.deleteDeployments(safe, selection);
18
+ if (result.ok)
19
+ evictDeletedDeployments(state, new Set(result.deleted.map((record) => record.deploymentId)));
20
+ return result;
21
+ }
22
+ /** Async compilation must rejoin authority before publishing a result into the disposable cache. */
23
+ export async function registryRecordStillExists(state, deploymentId) {
24
+ const current = state.store === undefined
25
+ ? state.records.get(deploymentId)
26
+ : await state.store.get(deploymentId);
27
+ if (current !== undefined && current.archivedAt === undefined)
28
+ return true;
29
+ evictDeletedDeployments(state, new Set([deploymentId]));
30
+ return false;
31
+ }
32
+ /** Refresh warmed cache entries from authority and coalesce compilation on a cache miss. */
33
+ export async function readRegistryTarget(state, deploymentId, inflight, targetForRecord, load) {
34
+ if (state.records.get(deploymentId)?.archivedAt !== undefined)
35
+ return undefined;
36
+ if (state.servers.has(deploymentId)) {
37
+ const record = state.store
38
+ ? await state.store.get(deploymentId)
39
+ : state.records.get(deploymentId);
40
+ if (record === undefined)
41
+ evictDeletedDeployments(state, new Set([deploymentId]));
42
+ return record === undefined || record.archivedAt !== undefined
43
+ ? undefined
44
+ : targetForRecord(record);
45
+ }
46
+ const pending = inflight.get(deploymentId);
47
+ if (pending)
48
+ return pending;
49
+ const promise = load(deploymentId).finally(() => {
50
+ inflight.delete(deploymentId);
51
+ });
52
+ inflight.set(deploymentId, promise);
53
+ return promise;
54
+ }
55
+ /** No-store records are authority, so stale reconciliation must never repopulate absent records. */
56
+ export async function reconcileRegistryTarget(state, record, context) {
57
+ let loaded = false;
58
+ const target = await targetForPersistedRecord(record, {
59
+ ...context,
60
+ load: () => {
61
+ loaded = true;
62
+ return context.load();
63
+ },
64
+ records: state.records,
65
+ servers: state.servers,
66
+ recordStillPresent: () => state.store !== undefined || state.records.has(record.deploymentId),
67
+ });
68
+ // A cold load already checks store authority just before publication; warm reconciliation must too.
69
+ // No async presence snapshot in no-store mode: deletion and publication share one synchronous turn.
70
+ const exists = state.store === undefined
71
+ ? state.records.has(record.deploymentId)
72
+ : loaded || (await registryRecordStillExists(state, record.deploymentId));
73
+ return exists ? target : undefined;
74
+ }
75
+ //# sourceMappingURL=registry-deletion.js.map
@@ -166,8 +166,10 @@ export async function setRegistryDeploymentLock(state, ref, serverVersion, expec
166
166
  state.records.set(result.record.deploymentId, result.record);
167
167
  return result;
168
168
  }
169
- const active = await activeRecordVersion(state, safe, safeVersion);
170
- if (active === undefined)
169
+ const observed = await activeRecordVersion(state, safe, safeVersion);
170
+ // The lookup yields; a concurrent deletion or pointer move must win before any cache write.
171
+ const active = observed === undefined ? undefined : state.records.get(observed.deploymentId);
172
+ if (active === undefined || !active.active || active.archivedAt !== undefined)
171
173
  return { ok: false, reason: 'no_active_deployment' };
172
174
  if (active.deploymentId !== expectedDeploymentId)
173
175
  return { ok: false, reason: 'conflict' };
@@ -110,6 +110,8 @@ function bindAppPackageSnapshot(served, value) {
110
110
  }
111
111
  /** Reconcile one persisted record with its cached served target without trusting stale auth projections. */
112
112
  export async function targetForPersistedRecord(record, state) {
113
+ if (state.recordStillPresent?.() === false)
114
+ return undefined;
113
115
  const cached = state.servers.get(record.deploymentId);
114
116
  const cachedSchemaVersion = cached === undefined ? undefined : targetPolicyVersions.get(cached);
115
117
  state.records.set(record.deploymentId, record);
@@ -127,6 +129,8 @@ export async function targetForPersistedRecord(record, state) {
127
129
  (await state.hasCustomerAuthConflict(record, cached.served.artifact.server.auth))) {
128
130
  return undefined;
129
131
  }
132
+ if (state.recordStillPresent?.() === false)
133
+ return undefined;
130
134
  if (cachedSchemaVersion === record.schemaVersion && servedTargetMatchesRecord(cached, record))
131
135
  return cached;
132
136
  const reconciled = servedTargetFor(record, cached.served, state.customerVerifierFactory);
@@ -96,6 +96,7 @@ export interface AccessUpdateOptions {
96
96
  readonly serverVersion?: string | undefined;
97
97
  }
98
98
  export interface ServerRegistryOptions {
99
+ readonly capabilities?: import('@noodle-borg/managed-capabilities').CapabilityService;
99
100
  readonly nativeRecords?: NativeRecordConnectorFactory;
100
101
  /** Pure renderer injection used for deployment-bound package construction and failure testing. */
101
102
  readonly renderAppPackage?: AppPackageRenderer;
@@ -9,6 +9,7 @@ import type { ServedTarget } from '@noodle-borg/transport-http';
9
9
  import type { NativeRecordConnectorFactory } from './native-record-connector.js';
10
10
  import { type ActiveDeployProvenance } from './registry-helpers.js';
11
11
  import type { AccessUpdateOptions, AccessUpdateResult, DeployOptions, DeployPreflightResult, RecoverResult, RunDeployResult, ServerRegistryOptions } from './registry-types.js';
12
+ import type { DeploymentDeleteResult, DeploymentDeleteSelection } from './store.js';
12
13
  import { type AppArchiveResult, type AppRestoreResult, type AppSummary, type ArtifactStore, type ConfigStore, type CustomerAuthRestoreProjection, type DeploymentListFilter, type DeploymentLock, type DeploymentLockUpdateResult, type DeploymentStatus, type DeploymentSummary, type DeployRecord, type EnvSummary, type ProductionEnvironmentChange, type TenantRef } from './store.js';
13
14
  export type { RollbackResult } from '@noodle-borg/control-plane/portable';
14
15
  export type { DeploymentPackage } from './registry-package.js';
@@ -29,6 +30,7 @@ export declare class ServerRegistry {
29
30
  /** Read-only deploy validation used by the CLI before config writes, asset upload, or persistence. */
30
31
  preflightDeploy(tenant: TenantRef, manifest: string, options?: DeployOptions): Promise<DeployPreflightResult>;
31
32
  recover(): Promise<RecoverResult>;
33
+ deleteDeployments(ref: TenantRef, selection: DeploymentDeleteSelection): Promise<DeploymentDeleteResult>;
32
34
  get(deploymentId: string): Promise<ServedTarget | undefined>;
33
35
  /** Resolve a deployment-id data-plane route only while that exact record is actively serving. */
34
36
  getServing(deploymentId: string): Promise<ServedTarget | undefined>;
@@ -6,6 +6,7 @@ import { DeploymentPolicyChangedError, UnsupportedDeploymentRecordVersionError,
6
6
  import { defaultActiveRecord } from './deployment-versioning.js';
7
7
  import { updateRegistryAccess } from './registry-access.js';
8
8
  import { compilePersistedRegistryRecord, compileRegistryTarget, loadPersistedRegistryTarget, } from './registry-compile.js';
9
+ import { deleteRegistryDeployments, readRegistryTarget, reconcileRegistryTarget, } from './registry-deletion.js';
9
10
  import { createRegistryDeployRecord, deployedRecordResult, deploymentWriteVersionError, preflightRegistryDeploy, replacementDeploymentPolicy, resolveDeployAttempt, withDeploymentConfiguration, } from './registry-deploy-transaction.js';
10
11
  import { deploymentOwnerSubject } from './registry-helpers.js';
11
12
  import { registryDeploymentPackage, renderDeploymentPackageSnapshot } from './registry-package.js';
@@ -13,7 +14,7 @@ import { recoverRegistryRecords } from './registry-recover.js';
13
14
  import { rollbackDeployment, rollbackWithMappedErrors } from './registry-rollback.js';
14
15
  import { activeRecord, activeRecordVersion, persistDeployRecord, reconcilePlatformAccountResetRegistryCache, registryAppArchivedAt, registryArchiveApp, registryCustomerAuthRestoreProjections, registryGetApp, registryGetDeployment, registryGetEnvironment, registryListApps, registryListEnvironments, registryRestoreApp, registrySetProductionEnvironment, registrySweepArchived, setRegistryDeploymentLock, } from './registry-state.js';
15
16
  import { deploymentStatusFor } from './registry-status.js';
16
- import { deploymentSourceFor, rebindNativeRecords, servedTargetFor, targetForPersistedRecord, tenantDeploymentKey, tenantKey, } from './registry-targets.js';
17
+ import { deploymentSourceFor, rebindNativeRecords, servedTargetFor, tenantDeploymentKey, tenantKey, } from './registry-targets.js';
17
18
  import { withBuiltinStateCatalog } from './state-catalog.js';
18
19
  import { deploymentSummary, matchesDeploymentFilter, validateDeploymentListFilter, } from './store/records.js';
19
20
  import { InMemoryConfigStore, validateTenantRef, } from './store.js';
@@ -31,6 +32,7 @@ export class ServerRegistry {
31
32
  #platformCatalog;
32
33
  #platformConnectors;
33
34
  #nativeRecords;
35
+ #capabilities;
34
36
  #stateHandleStoreFactory;
35
37
  #delegatedCredentialStore;
36
38
  #sealCustomerCredential;
@@ -60,6 +62,7 @@ export class ServerRegistry {
60
62
  this.#platformCatalog = withBuiltinStateCatalog(options.platformCatalog ?? []);
61
63
  this.#platformConnectors = options.platformConnectors ?? [];
62
64
  this.#nativeRecords = options.nativeRecords;
65
+ this.#capabilities = options.capabilities;
63
66
  this.#stateHandleStoreFactory = options.stateHandleStoreFactory;
64
67
  this.#delegatedCredentialStore = options.delegatedCredentialStore;
65
68
  this.#sealCustomerCredential = options.sealCustomerCredential;
@@ -310,6 +313,7 @@ export class ServerRegistry {
310
313
  stateHandleStoreFactory: this.#stateHandleStoreFactory,
311
314
  platformConnectors: this.#platformConnectors,
312
315
  nativeRecords: this.#nativeRecords,
316
+ capabilities: this.#capabilities,
313
317
  policyGate: this.#policyGate,
314
318
  appPackageRenderer: this.#appPackageRenderer,
315
319
  knowledgeSearch: this.#knowledgeSearch,
@@ -318,26 +322,11 @@ export class ServerRegistry {
318
322
  #compilePersistedRecord(record) {
319
323
  return compilePersistedRegistryRecord(record, this.#compileTarget.bind(this));
320
324
  }
325
+ deleteDeployments(ref, selection) {
326
+ return deleteRegistryDeployments(this.#stateView(), ref, selection);
327
+ }
321
328
  async get(deploymentId) {
322
- if (this.#records.get(deploymentId)?.archivedAt !== undefined)
323
- return undefined;
324
- const cached = this.#servers.get(deploymentId);
325
- if (cached) {
326
- const record = this.#store
327
- ? await this.#store.get(deploymentId)
328
- : this.#records.get(deploymentId);
329
- return record === undefined || record.archivedAt !== undefined
330
- ? undefined
331
- : this.#targetForPersistedRecord(record);
332
- }
333
- const inflight = this.#inflight.get(deploymentId);
334
- if (inflight)
335
- return inflight;
336
- const promise = this.#loadAndCompile(deploymentId).finally(() => {
337
- this.#inflight.delete(deploymentId);
338
- });
339
- this.#inflight.set(deploymentId, promise);
340
- return promise;
329
+ return readRegistryTarget(this.#stateView(), deploymentId, this.#inflight, (record) => this.#targetForPersistedRecord(record), (id) => this.#loadAndCompile(id));
341
330
  }
342
331
  /** Resolve a deployment-id data-plane route only while that exact record is actively serving. */
343
332
  async getServing(deploymentId) {
@@ -499,9 +488,7 @@ export class ServerRegistry {
499
488
  return this.#knowledgeSearch;
500
489
  }
501
490
  async #targetForPersistedRecord(record) {
502
- return targetForPersistedRecord(record, {
503
- records: this.#records,
504
- servers: this.#servers,
491
+ return reconcileRegistryTarget(this.#stateView(), record, {
505
492
  ...(this.#customerVerifierFactory
506
493
  ? { customerVerifierFactory: this.#customerVerifierFactory }
507
494
  : {}),
@@ -3,6 +3,7 @@ import { assistantCoreModelMessages, assistantTurnModelContextMessages, requestA
3
3
  import { assistantGuideModelContext, assistantModelToolOncePerSession, assistantModelToolRequiredWhenVisible, assistantOmittedToolResult, assistantViewAvailableData, dispatchAssistantTool, projectAssistantGuide, publicSurfaceOf, recoverableAssistantView, remainingTurnTokens, selectAssistantModelTools, toolTouchesDelegatedAuth, withAssistantSessionExecutionAuthority, } from '@noodle-borg/assistant-gateway/portable';
4
4
  import { validateJsonSchemaWithDefaults } from '@noodle-borg/compiler';
5
5
  import { guardedFetch } from '@noodle-borg/connector-http';
6
+ import { CapabilityBudget } from '@noodle-borg/managed-capabilities';
6
7
  import { evaluateToolAuthorization } from '@noodle-borg/protocol';
7
8
  import { interceptForElevation, offersSignIn } from './assistant-elevation.js';
8
9
  import { assistantKnowledgeModelTools, executeAssistantKnowledgeSearch, findAssistantKnowledgeComponent, KNOWLEDGE_CITATION_GUIDANCE, resolveAssistantKnowledge, } from './assistant-knowledge.js';
@@ -98,6 +99,7 @@ export async function runAgentTurn(target, session, message, context, deps, emit
98
99
  // Across every step of this turn, not per step: eight tool calls is eight, however the model splits
99
100
  // them, or a model that loops one call per step would spend the budget a step at a time.
100
101
  let toolCallsThisTurn = 0;
102
+ const capabilityBudget = new CapabilityBudget();
101
103
  let omittedToolRecoveries = 0;
102
104
  let remainingTokens = binding.requestPolicy?.maxTokensPerTurn;
103
105
  const turnSignal = binding.requestPolicy?.maxTurnMs === undefined
@@ -247,7 +249,7 @@ export async function runAgentTurn(target, session, message, context, deps, emit
247
249
  artifact: target.served.artifact,
248
250
  tool,
249
251
  arguments: args,
250
- executeDeps: withAssistantSessionExecutionAuthority(target.served.deps, target.served.artifact, session),
252
+ executeDeps: withAssistantSessionExecutionAuthority({ ...target.served.deps, capabilityBudget }, target.served.artifact, session),
251
253
  caller: session.caller,
252
254
  context,
253
255
  session,
@@ -273,7 +275,8 @@ export async function runAgentTurn(target, session, message, context, deps, emit
273
275
  arguments: args,
274
276
  }, (failure) => deps.logger?.warn('assistant.view.unresolved', { ...failure }));
275
277
  if (view) {
276
- await deps.store.replaceLatestView(session.id, recoverableAssistantView(view));
278
+ if (!dispatch.ephemeral)
279
+ await deps.store.replaceLatestView(session.id, recoverableAssistantView(view));
277
280
  emit({ event: 'view_available', data: { ...view } });
278
281
  }
279
282
  messages.push({
@@ -1,5 +1,5 @@
1
1
  import { AssistantInteractionCapacityError, assistantConfirmationReview, assistantConfirmationTransition, assistantInputTransition, assistantInteractionTool, assistantPreparedArgumentReview, assistantPreparedToolContinuation, assistantSafeOutput, assistantToolContinuation, assistantViewAvailableData, auditAssistantInteraction, boundedAssistantExecution, recoverableAssistantView, withAssistantSessionExecutionAuthority, } from '@noodle-borg/assistant-gateway/portable';
2
- import { executePreparedTool, executeToolInteractive, resumeTool, resumeToolPreparation, } from '@noodle-borg/runtime';
2
+ import { executePreparedTool, executeToolInteractive, hasEphemeralEvidence, resumeTool, resumeToolPreparation, } from '@noodle-borg/runtime';
3
3
  import { readJsonBody, sendJson } from '@noodle-borg/transport-http';
4
4
  import { applyBrowserCors, authenticateSession, now, } from './assistant.js';
5
5
  import { replayOrConflict, writeUnknownOutcome } from './assistant-interaction-replay.js';
@@ -347,7 +347,9 @@ async function finishExecution(res, deps, session, interaction, context, result,
347
347
  }
348
348
  const completedTool = target.served.artifact.tools.find((tool) => tool.name === toolName);
349
349
  const safeOutput = assistantSafeOutput(completedTool?.outputSchema, result.output);
350
- const completed = await completeAssistantInteractionExecution(deps, session, interaction, 'succeeded', 'tool_completed', toolName, { result: safeOutput });
350
+ const ephemeral = hasEphemeralEvidence(result.output);
351
+ const retainedOutput = ephemeral ? { evidenceRetained: false } : safeOutput;
352
+ const completed = await completeAssistantInteractionExecution(deps, session, interaction, 'succeeded', 'tool_completed', toolName, { result: retainedOutput });
351
353
  if (!completed)
352
354
  return writeUnknownOutcome(res, deps, session, interaction, 'accept');
353
355
  await auditAssistantInteraction({
@@ -364,7 +366,7 @@ async function finishExecution(res, deps, session, interaction, context, result,
364
366
  // Model-facing scaffolding: the panel saw the tool_completed card, never this row's tool JSON.
365
367
  {
366
368
  role: 'assistant',
367
- content: `Completed ${toolName}: ${JSON.stringify(safeOutput)}`,
369
+ content: `Completed ${toolName}: ${JSON.stringify(retainedOutput)}`,
368
370
  kind: 'narration',
369
371
  },
370
372
  ]);
@@ -377,7 +379,8 @@ async function finishExecution(res, deps, session, interaction, context, result,
377
379
  result: safeOutput,
378
380
  }, (failure) => deps.logger?.warn('assistant.view.unresolved', { ...failure }));
379
381
  if (view) {
380
- await deps.store.replaceLatestView(session.id, recoverableAssistantView(view));
382
+ if (!ephemeral)
383
+ await deps.store.replaceLatestView(session.id, recoverableAssistantView(view));
381
384
  writeViewAvailable(res, view);
382
385
  }
383
386
  await narrateResolvedInteraction(res, deps, target, session, toolName, 'accept', safeOutput, context, suggestions);
@@ -2,9 +2,23 @@ import { applySecurityHeaders, enforceHttps, } from '@noodle-borg/transport-http
2
2
  import { respondRouteError } from '../http-util.js';
3
3
  import { handleDeploy } from './control-plane.js';
4
4
  import { handleDeployPreflight } from './deploy-preflight.js';
5
+ import { handleDeploymentDelete, parseVersionDeletePath } from './deployment-delete.js';
5
6
  import { handleDeploymentLockUpdate } from './deployment-lock.js';
6
- import { parseTenantDeploymentLockPath, parseTenantDeployPath, parseTenantDeployPreflightPath, } from './paths.js';
7
+ import { parseDeploymentItemPath, parseTenantDeploymentLockPath, parseTenantDeployPath, parseTenantDeployPreflightPath, } from './paths.js';
7
8
  export function dispatchDeployRoutes(req, res, url, deps) {
9
+ if (req.method === 'DELETE') {
10
+ const item = parseDeploymentItemPath(url.pathname);
11
+ const deletion = item
12
+ ? { kind: 'deployment', ...item }
13
+ : parseVersionDeletePath(url.pathname);
14
+ if (deletion) {
15
+ applySecurityHeaders(res, deps.tls);
16
+ if (enforceHttps(req, res, deps.tls))
17
+ return true;
18
+ handleDeploymentDelete(req, res, deletion, { ...deps, maxBody: deps.maxBody.control }).catch((error) => respondRouteError(deps.logger, res, 'deployment-delete.error', error));
19
+ return true;
20
+ }
21
+ }
8
22
  const deploymentLock = parseTenantDeploymentLockPath(url.pathname);
9
23
  if (req.method === 'PATCH' && deploymentLock !== undefined) {
10
24
  applySecurityHeaders(res, deps.tls);
@@ -0,0 +1,53 @@
1
+ import { deleteDeploymentOperation, } from '@noodle-borg/control-plane/portable';
2
+ import { normalizeServerVersion } from '@noodle-borg/module';
3
+ import { readJsonBody, sendJson } from '@noodle-borg/transport-http';
4
+ import { deploymentDeleteResponseSchema, deploymentVersionDeleteRequestSchema, } from '@noodle-borg/wire-contracts';
5
+ import { validateTenantRef } from '../store.js';
6
+ import { authorizeControlPlane } from './control-plane.js';
7
+ /** An exact numeric version, or the legacy unversioned scope; never semantic-version equivalence. */
8
+ export function parseVersionDeletePath(pathname) {
9
+ const match = /^\/v1\/orgs\/([^/]+)\/apps\/([^/]+)\/envs\/([^/]+)\/versions\/([^/]+)$/.exec(pathname);
10
+ if (!match)
11
+ return undefined;
12
+ try {
13
+ const target = validateTenantRef({
14
+ org: decodeURIComponent(match[1]),
15
+ app: decodeURIComponent(match[2]),
16
+ env: decodeURIComponent(match[3]),
17
+ });
18
+ const version = decodeURIComponent(match[4]);
19
+ return {
20
+ kind: 'version',
21
+ org: target.org,
22
+ target,
23
+ ...(version === 'legacy' ? {} : { serverVersion: normalizeServerVersion(version) }),
24
+ };
25
+ }
26
+ catch {
27
+ return undefined;
28
+ }
29
+ }
30
+ export async function handleDeploymentDelete(req, res, route, deps) {
31
+ const identity = await authorizeControlPlane(req, res, deps.gate, { requireIdentity: true });
32
+ if (identity === false)
33
+ return;
34
+ let expectedDeploymentIds;
35
+ if (route.kind === 'version') {
36
+ const body = await readJsonBody(req, deps.maxBody);
37
+ if (!body.ok)
38
+ return sendJson(res, body.status, { error: body.error });
39
+ const parsed = deploymentVersionDeleteRequestSchema.safeParse(body.value);
40
+ if (!parsed.success)
41
+ return sendJson(res, 400, { error: 'Invalid version deletion confirmation.' });
42
+ expectedDeploymentIds = parsed.data.expectedDeploymentIds;
43
+ }
44
+ const result = await deleteDeploymentOperation(deps, {
45
+ actor: identity,
46
+ scope: route,
47
+ ...(expectedDeploymentIds === undefined ? {} : { expectedDeploymentIds }),
48
+ });
49
+ return result.ok
50
+ ? sendJson(res, 200, deploymentDeleteResponseSchema.parse(result.view))
51
+ : sendJson(res, result.status, { code: result.code, error: result.error });
52
+ }
53
+ //# sourceMappingURL=deployment-delete.js.map
@@ -3,6 +3,7 @@ import { InMemoryControlPlaneStore } from '@noodle-borg/control-plane/portable';
3
3
  import { InMemoryAssetStore } from './assets.js';
4
4
  import { createCustomerVerifierFactory, createHostedCustomerVerifierFactory, createLocalDevtoolsCustomerVerifierFactory, } from './customer-verifier.js';
5
5
  import { createLocalDevtoolsDelegatedCredentialSource } from './local-devtools-delegated-credentials.js';
6
+ import { localWebCapabilities } from './local-web-capabilities.js';
6
7
  import { ServerRegistry } from './registry.js';
7
8
  import { isLoopbackHost } from './serve-resource-auth.js';
8
9
  import { createServiceHandler } from './service.js';
@@ -48,7 +49,9 @@ export async function serveLocalService(options = {}) {
48
49
  const delegatedCredentials = directCustomerAuth
49
50
  ? createLocalDevtoolsDelegatedCredentialSource()
50
51
  : undefined;
52
+ const capabilities = options.runtime?.capabilities ?? localWebCapabilities();
51
53
  const registry = new ServerRegistry(undefined, undefined, configStore, {
54
+ capabilities,
52
55
  customerVerifierFactory,
53
56
  ...(delegatedCredentials === undefined
54
57
  ? {}
@@ -62,6 +65,17 @@ export async function serveLocalService(options = {}) {
62
65
  : { localDevtoolsDelegatedExchange: options.localDevtoolsDelegatedExchange }),
63
66
  });
64
67
  const handler = createServiceHandler(registry, {
68
+ capabilities,
69
+ deployGate: {
70
+ authorize: () => ({
71
+ ok: true,
72
+ identity: {
73
+ subject: 'local-operator',
74
+ email: 'local-operator@localhost',
75
+ superAdmin: true,
76
+ },
77
+ }),
78
+ },
65
79
  controlPlaneStore,
66
80
  configStore,
67
81
  assetStore: new InMemoryAssetStore(),
@@ -1,10 +1,20 @@
1
1
  import { resolveMcpSubdomainTenant } from '@noodle-borg/control-plane/portable';
2
2
  import { normalizePublicBaseDomain, parseCanonicalPublicMcpUrl, parseLegacyTenantMcpPath, } from '@noodle-borg/module';
3
3
  import { resolveTenantBridgeAuthVariables } from './managed-config-expressions.js';
4
+ import { createHostedMcpRequestStateManager } from './mcp-protocol-runtime.js';
4
5
  import { resolveConfigScope, } from './store.js';
5
6
  export function isLoopbackHost(host) {
6
7
  return host === '127.0.0.1' || host === '::1' || host === 'localhost';
7
8
  }
9
+ export function resolveMcpRequestState(options, secretBox) {
10
+ return (options.mcpRequestState ??
11
+ createHostedMcpRequestStateManager({
12
+ ...(options.wrappingMasterKey === undefined && options.secretMasterKey !== undefined
13
+ ? { secretMasterKey: options.secretMasterKey }
14
+ : {}),
15
+ ...(secretBox === undefined ? {} : { secretBox }),
16
+ }));
17
+ }
8
18
  export function assertLocalDevtoolsServiceBoundary(host, options) {
9
19
  if (options.localDevtoolsDirectFirebaseAuth === true && !isLoopbackHost(host)) {
10
20
  throw new Error('local Devtools Firebase verification requires a loopback service bind');
@@ -2,6 +2,7 @@ import { InMemoryDailyCounterStore } from '@noodle-borg/admission-limits/portabl
2
2
  import { InMemoryAssistantAppearanceSettingsStore, InMemoryAssistantStore, } from '@noodle-borg/assistant-gateway/portable';
3
3
  import { allowAllGate, InMemoryControlPlaneStore } from '@noodle-borg/control-plane/portable';
4
4
  import { dispatchKnowledgeRequest } from '@noodle-borg/knowledge-operations/portable';
5
+ import { capabilityOperatorRoute } from '@noodle-borg/managed-capabilities';
5
6
  import { OPENAI_APPS_CHALLENGE_PATH } from '@noodle-borg/module';
6
7
  import { applySecurityHeaders, createMcpRouter, enforceHttps, noopLogger, sendJson, } from '@noodle-borg/transport-http';
7
8
  import { AnonymousConsumerLimiter, createAdmissionGate } from './admission.js';
@@ -163,7 +164,12 @@ export function createServiceHandler(registry, options = {}) {
163
164
  });
164
165
  archiveSweeper.maybeSweep();
165
166
  const moduleRouteDeps = {
166
- routes: moduleHost.routes,
167
+ routes: [
168
+ ...moduleHost.routes,
169
+ ...(options.capabilities === undefined
170
+ ? []
171
+ : [capabilityOperatorRoute(options.capabilities)]),
172
+ ],
167
173
  logger,
168
174
  gate,
169
175
  controlPlane,
@@ -1,7 +1,8 @@
1
- import type { ActiveAccessUpdateInput, AppArchiveResult, AppRestorePrecondition, AppRestoreResult, AppSummary, ArtifactStore, DeploymentActivationPrecondition, DeploymentActivationResult, DeploymentListFilter, DeploymentLock, DeploymentLockUpdateResult, DeploymentPolicyPrecondition, DeploymentSummary, DeployRecord, EnvSummary, ProductionEnvironmentChange, TenantAuthConfig, TenantRef } from '../store.js';
1
+ import type { ActiveAccessUpdateInput, AppArchiveResult, AppRestorePrecondition, AppRestoreResult, AppSummary, ArtifactStore, DeploymentActivationPrecondition, DeploymentActivationResult, DeploymentDeleteResult, DeploymentDeleteSelection, DeploymentListFilter, DeploymentLock, DeploymentLockUpdateResult, DeploymentPolicyPrecondition, DeploymentSummary, DeployRecord, EnvSummary, ProductionEnvironmentChange, TenantAuthConfig, TenantRef } from '../store.js';
2
2
  /** In-memory store: keeps records in a Map. Used by tests and as an explicit, non-persistent option. */
3
3
  export declare class InMemoryArtifactStore implements ArtifactStore {
4
4
  #private;
5
+ deleteDeployments(ref: TenantRef, selection: DeploymentDeleteSelection): Promise<DeploymentDeleteResult>;
5
6
  append(record: DeployRecord, precondition?: DeploymentPolicyPrecondition): Promise<void>;
6
7
  loadAll(): Promise<readonly DeployRecord[]>;
7
8
  get(deploymentId: string): Promise<DeployRecord | undefined>;
@@ -2,6 +2,7 @@ import { normalizeServerVersion } from '@noodle-borg/module';
2
2
  import { assertSameAppPackageSnapshot, sanitizeDeployRecordAppPackageSnapshot, } from '../app-package-snapshot.js';
3
3
  import { assertCustomerAuthRestorePrecondition, assertUniqueActiveCustomerAuthAudienceBindings, findActiveCustomerAuthAudienceConflict, requiresCustomerAuthProjection, } from '../customer-auth-audience-binding.js';
4
4
  import { matchesDeploymentActivation } from '../deployment-activation-precondition.js';
5
+ import { planDeploymentDeletion } from '../deployment-deletion.js';
5
6
  import { assertDeploymentActivationUnlocked, assertDeploymentAppendUnlocked, } from '../deployment-lock.js';
6
7
  import { assertDeploymentAppendPolicy, assertDeploymentAppendVersion, } from '../deployment-record-version.js';
7
8
  import { defaultActiveRecord, sameDeploymentScope, sameTenantRecord, } from '../deployment-versioning.js';
@@ -11,6 +12,13 @@ import { validateSlug, validateTenantRef } from './validate.js';
11
12
  export class InMemoryArtifactStore {
12
13
  #records = new Map();
13
14
  #productionEnvironments = new Map();
15
+ deleteDeployments(ref, selection) {
16
+ const result = planDeploymentDeletion([...this.#records.values()], ref, selection);
17
+ if (result.ok)
18
+ for (const record of result.deleted)
19
+ this.#records.delete(record.deploymentId);
20
+ return Promise.resolve(result);
21
+ }
14
22
  async append(record, precondition) {
15
23
  assertDeploymentAppendVersion([...this.#records.values()], record);
16
24
  assertDeploymentAppendPolicy([...this.#records.values()], record, precondition);