@noodleseed/one 0.166.0 → 0.168.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 (120) hide show
  1. package/dist/commands/author-loop.d.ts.map +1 -1
  2. package/dist/commands/author-loop.js +18 -3
  3. package/dist/commands/author-loop.js.map +1 -1
  4. package/dist/commands/deploy-ops.d.ts.map +1 -1
  5. package/dist/commands/deploy-ops.js +21 -2
  6. package/dist/commands/deploy-ops.js.map +1 -1
  7. package/dist/commands/deploy-status-ops.d.ts.map +1 -1
  8. package/dist/commands/deploy-status-ops.js +16 -0
  9. package/dist/commands/deploy-status-ops.js.map +1 -1
  10. package/dist/control-plane-request.d.ts +2 -0
  11. package/dist/control-plane-request.d.ts.map +1 -1
  12. package/dist/control-plane-request.js +18 -0
  13. package/dist/control-plane-request.js.map +1 -1
  14. package/dist/deploy.d.ts +2 -1
  15. package/dist/deploy.d.ts.map +1 -1
  16. package/dist/deploy.js +15 -0
  17. package/dist/deploy.js.map +1 -1
  18. package/dist/dev.d.ts +2 -0
  19. package/dist/dev.d.ts.map +1 -1
  20. package/dist/dev.js +12 -1
  21. package/dist/dev.js.map +1 -1
  22. package/dist/devtools-auth-session.d.ts +5 -0
  23. package/dist/devtools-auth-session.d.ts.map +1 -1
  24. package/dist/devtools-auth-session.js +41 -2
  25. package/dist/devtools-auth-session.js.map +1 -1
  26. package/dist/devtools-auth-types.d.ts +2 -1
  27. package/dist/devtools-auth-types.d.ts.map +1 -1
  28. package/dist/devtools-auth-types.js.map +1 -1
  29. package/dist/devtools-auth-ui.d.ts +1 -1
  30. package/dist/devtools-auth-ui.d.ts.map +1 -1
  31. package/dist/devtools-auth-ui.js +13 -5
  32. package/dist/devtools-auth-ui.js.map +1 -1
  33. package/dist/devtools-harness.d.ts +1 -0
  34. package/dist/devtools-harness.d.ts.map +1 -1
  35. package/dist/devtools-harness.js +12 -7
  36. package/dist/devtools-harness.js.map +1 -1
  37. package/dist/devtools-preview.d.ts +1 -0
  38. package/dist/devtools-preview.d.ts.map +1 -1
  39. package/dist/devtools-preview.js +21 -6
  40. package/dist/devtools-preview.js.map +1 -1
  41. package/dist/devtools-rpc-forwarder.d.ts +1 -0
  42. package/dist/devtools-rpc-forwarder.d.ts.map +1 -1
  43. package/dist/devtools-rpc-forwarder.js +2 -1
  44. package/dist/devtools-rpc-forwarder.js.map +1 -1
  45. package/dist/preview-session.d.ts +1 -0
  46. package/dist/preview-session.d.ts.map +1 -1
  47. package/dist/preview-session.js +1 -0
  48. package/dist/preview-session.js.map +1 -1
  49. package/node_modules/@noodle-borg/agent-kit/dist/behavior-skills.js +4 -2
  50. package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +3 -3
  51. package/node_modules/@noodle-borg/agent-kit/dist/plugin-bootstrap-skill.js +8 -1
  52. package/node_modules/@noodle-borg/agent-kit/dist/skill-authoring-auth-ref.js +5 -2
  53. package/node_modules/@noodle-borg/agent-kit/dist/skill-embedded-assistant-ref.js +20 -0
  54. package/node_modules/@noodle-borg/agent-kit/dist/skill-router.js +9 -2
  55. package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
  56. package/node_modules/@noodle-borg/agent-packaging/dist/product-skill-types.d.ts +1 -0
  57. package/node_modules/@noodle-borg/agent-packaging/dist/product-skill-validation.js +4 -0
  58. package/node_modules/@noodle-borg/app-package/dist/schema.js +1 -0
  59. package/node_modules/@noodle-borg/app-package/dist/types.d.ts +1 -0
  60. package/node_modules/@noodle-borg/authoring/dist/server.d.ts +3 -1
  61. package/node_modules/@noodle-borg/authoring/dist/server.js +7 -6
  62. package/node_modules/@noodle-borg/capabilities/dist/index.js +7 -0
  63. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-local-authoring.js +8 -0
  64. package/node_modules/@noodle-borg/compiler/dist/app-package/compile.js +9 -1
  65. package/node_modules/@noodle-borg/compiler/dist/artifact/catalog-emission.js +3 -0
  66. package/node_modules/@noodle-borg/compiler/dist/artifact/types.d.ts +2 -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/manifest/schema.d.ts +5 -0
  70. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +12 -2
  71. package/node_modules/@noodle-borg/protocol/dist/handlers/tools.js +3 -0
  72. package/node_modules/@noodle-borg/protocol/dist/index.d.ts +1 -0
  73. package/node_modules/@noodle-borg/protocol/dist/index.js +1 -0
  74. package/node_modules/@noodle-borg/protocol/dist/mapping.d.ts +5 -1
  75. package/node_modules/@noodle-borg/protocol/dist/mapping.js +15 -7
  76. package/node_modules/@noodle-borg/protocol/dist/sdk-server.d.ts +2 -0
  77. package/node_modules/@noodle-borg/protocol/dist/tool-discovery.d.ts +14 -0
  78. package/node_modules/@noodle-borg/protocol/dist/tool-discovery.js +12 -0
  79. package/node_modules/@noodle-borg/protocol/dist/v2/server.js +3 -0
  80. package/node_modules/@noodle-borg/service/dist/build-info.js +4 -2
  81. package/node_modules/@noodle-borg/service/dist/deployment-access-mode.js +24 -0
  82. package/node_modules/@noodle-borg/service/dist/deployment-activation-precondition.js +27 -0
  83. package/node_modules/@noodle-borg/service/dist/deployment-authentication.js +2 -2
  84. package/node_modules/@noodle-borg/service/dist/deployment-execution.js +2 -2
  85. package/node_modules/@noodle-borg/service/dist/deployment-record-version.js +26 -2
  86. package/node_modules/@noodle-borg/service/dist/registry-access.js +33 -31
  87. package/node_modules/@noodle-borg/service/dist/registry-deploy-transaction.js +97 -22
  88. package/node_modules/@noodle-borg/service/dist/registry-rollback.js +13 -1
  89. package/node_modules/@noodle-borg/service/dist/registry-state.d.ts +3 -7
  90. package/node_modules/@noodle-borg/service/dist/registry-state.js +6 -5
  91. package/node_modules/@noodle-borg/service/dist/registry-status.js +3 -4
  92. package/node_modules/@noodle-borg/service/dist/registry-targets.js +35 -28
  93. package/node_modules/@noodle-borg/service/dist/registry-types.d.ts +3 -1
  94. package/node_modules/@noodle-borg/service/dist/registry.js +40 -39
  95. package/node_modules/@noodle-borg/service/dist/routes/access.js +4 -2
  96. package/node_modules/@noodle-borg/service/dist/routes/auth-doctor-live.js +3 -2
  97. package/node_modules/@noodle-borg/service/dist/routes/control-plane.js +1 -0
  98. package/node_modules/@noodle-borg/service/dist/routes/deploy-preflight.js +2 -2
  99. package/node_modules/@noodle-borg/service/dist/serve-resource-auth.js +1 -1
  100. package/node_modules/@noodle-borg/service/dist/store/in-memory.d.ts +3 -8
  101. package/node_modules/@noodle-borg/service/dist/store/in-memory.js +6 -4
  102. package/node_modules/@noodle-borg/service/dist/store/json-file.d.ts +3 -8
  103. package/node_modules/@noodle-borg/service/dist/store/json-file.js +8 -6
  104. package/node_modules/@noodle-borg/service/dist/store/records.js +13 -2
  105. package/node_modules/@noodle-borg/service/dist/store.d.ts +19 -8
  106. package/node_modules/@noodle-borg/service/package.json +1 -1
  107. package/node_modules/@noodle-borg/transport-http/dist/handler.d.ts +2 -3
  108. package/node_modules/@noodle-borg/transport-http/dist/handler.js +3 -4
  109. package/node_modules/@noodle-borg/transport-http/dist/identity-authorization.d.ts +3 -2
  110. package/node_modules/@noodle-borg/transport-http/dist/identity-authorization.js +23 -12
  111. package/node_modules/@noodle-borg/transport-http/dist/index.d.ts +1 -0
  112. package/node_modules/@noodle-borg/transport-http/dist/serve-request.js +3 -0
  113. package/node_modules/@noodle-borg/transport-http/dist/target-authentication.d.ts +15 -0
  114. package/node_modules/@noodle-borg/transport-http/dist/target-authentication.js +9 -0
  115. package/node_modules/@noodle-borg/wire-contracts/dist/index.d.ts +6 -0
  116. package/node_modules/@noodle-borg/wire-contracts/dist/index.js +4 -1
  117. package/node_modules/@noodle-borg/wire-contracts/dist/service-info.d.ts +41 -0
  118. package/node_modules/@noodle-borg/wire-contracts/dist/service-info.js +26 -0
  119. package/package.json +2 -2
  120. package/node_modules/@noodle-borg/service/dist/routes/access-mode.js +0 -7
@@ -195,6 +195,7 @@ export async function handleDeploy(req, res, registry, options, maxBody, tenant,
195
195
  deploymentId: result.deploymentId,
196
196
  serverVersion: result.serverVersion,
197
197
  accessMode: result.accessMode,
198
+ ...(result.authentication !== undefined ? { authentication: result.authentication } : {}),
198
199
  ...(result.ownerSubject !== undefined ? { ownerSubject: result.ownerSubject } : {}),
199
200
  url: tenantMcpUrl(base, tenant, result.serverVersion, endpointOptions),
200
201
  defaultUrl: tenantMcpUrl(base, tenant, undefined, endpointOptions),
@@ -2,8 +2,8 @@ import { randomUUID } from 'node:crypto';
2
2
  import { cspFaultsInManifest } from '@noodle-borg/compiler';
3
3
  import { readDeployBody, sendJson } from '@noodle-borg/transport-http';
4
4
  import { DEPLOY_PREFLIGHT_CHECK_TIMEOUT_MS, deployPreflightRequestSchema, formatWireError, } from '@noodle-borg/wire-contracts';
5
+ import { isIdentityAccessMode } from '../deployment-access-mode.js';
5
6
  import { manifestUsesUserRoot } from '../registry-access.js';
6
- import { isIdentityAccessMode } from './access-mode.js';
7
7
  import { authorizeDeploymentOwnerSelection, authorizeTenantControl, developerGrantRouteAccess, } from './control-plane.js';
8
8
  // Per-process resource admission, not a tenant quota or distributed state authority.
9
9
  const activePreflights = new WeakSet();
@@ -91,7 +91,7 @@ export async function handleDeployPreflight(req, res, registry, options, maxBody
91
91
  });
92
92
  }
93
93
  const accessMode = parsed.accessMode ?? 'owner-only';
94
- if (isIdentityAccessMode(accessMode) && identity === undefined) {
94
+ if (isIdentityAccessMode(accessMode, parsed.manifest) && identity === undefined) {
95
95
  return sendJson(res, 401, {
96
96
  code: 'deployer_identity_required',
97
97
  error: `${accessMode} deployments require an authenticated deployer`,
@@ -44,7 +44,7 @@ export async function bridgeAuthForResource(registry, resource, allowedBaseDomai
44
44
  ? await registry.getActiveByTenant(tenant)
45
45
  : await registry.getActiveByTenantVersion(tenant, tenant.serverVersion);
46
46
  const auth = target?.served.artifact.server.auth;
47
- if (auth?.kind !== 'bridge')
47
+ if (target?.authentication?.kind !== 'customer' || auth?.kind !== 'bridge')
48
48
  return undefined;
49
49
  const variables = await registry.configStore.resolveConfigValues('variable', resolveConfigScope(tenant));
50
50
  return resolveTenantBridgeAuthVariables(auth, variables);
@@ -1,20 +1,15 @@
1
- import type { AccessMode } from '@noodle-borg/transport-http';
2
- import type { ActiveAccessUpdateInput, AppArchiveResult, AppRestorePrecondition, AppRestoreResult, AppSummary, ArtifactStore, DeploymentActivationResult, DeploymentListFilter, DeploymentLock, DeploymentLockUpdateResult, DeploymentSummary, DeployRecord, EnvSummary, ProductionEnvironmentChange, TenantAuthConfig, TenantRef } from '../store.js';
1
+ import type { ActiveAccessUpdateInput, AppArchiveResult, AppRestorePrecondition, AppRestoreResult, AppSummary, ArtifactStore, DeploymentActivationPrecondition, DeploymentActivationResult, DeploymentListFilter, DeploymentLock, DeploymentLockUpdateResult, DeploymentPolicyPrecondition, DeploymentSummary, DeployRecord, EnvSummary, ProductionEnvironmentChange, TenantAuthConfig, TenantRef } from '../store.js';
3
2
  /** In-memory store: keeps records in a Map. Used by tests and as an explicit, non-persistent option. */
4
3
  export declare class InMemoryArtifactStore implements ArtifactStore {
5
4
  #private;
6
- append(record: DeployRecord): Promise<void>;
5
+ append(record: DeployRecord, precondition?: DeploymentPolicyPrecondition): Promise<void>;
7
6
  loadAll(): Promise<readonly DeployRecord[]>;
8
7
  get(deploymentId: string): Promise<DeployRecord | undefined>;
9
8
  getActiveByTenant(ref: TenantRef): Promise<DeployRecord | undefined>;
10
9
  getActiveByTenantVersion(ref: TenantRef, serverVersion: string): Promise<DeployRecord | undefined>;
11
10
  setDeploymentLock(ref: TenantRef, serverVersion: string, expectedDeploymentId: string, deploymentLock: DeploymentLock | undefined): Promise<DeploymentLockUpdateResult>;
12
11
  findActiveCustomerAuthAudienceConflict(ref: TenantRef, auth: TenantAuthConfig): Promise<TenantRef | undefined>;
13
- activateDeployment(ref: TenantRef, deploymentId: string, precondition?: {
14
- readonly expectedAccessMode: AccessMode | undefined;
15
- readonly expectedSchemaVersion?: number;
16
- readonly serverAuth?: TenantAuthConfig;
17
- }, _options?: {
12
+ activateDeployment(ref: TenantRef, deploymentId: string, precondition?: DeploymentActivationPrecondition, _options?: {
18
13
  readonly automationId?: string;
19
14
  }): Promise<DeploymentActivationResult | undefined>;
20
15
  updateActiveAccess(ref: TenantRef, deploymentId: string, input: ActiveAccessUpdateInput): Promise<DeployRecord | undefined>;
@@ -1,8 +1,9 @@
1
1
  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
+ import { matchesDeploymentActivation } from '../deployment-activation-precondition.js';
4
5
  import { assertDeploymentActivationUnlocked, assertDeploymentAppendUnlocked, } from '../deployment-lock.js';
5
- import { assertDeploymentAppendVersion } from '../deployment-record-version.js';
6
+ import { assertDeploymentAppendPolicy, assertDeploymentAppendVersion, } from '../deployment-record-version.js';
6
7
  import { defaultActiveRecord, sameDeploymentScope, sameTenantRecord, } from '../deployment-versioning.js';
7
8
  import { appArchivedAt, deploymentSummary, matchesDeploymentFilter, paginateAppSummaries, planAppArchive, planAppRestore, planArchivedAppSweep, preserveDeploymentOwnerState, resolveActiveAccessUpdate, resolveProductionEnvironment, summarizeApps, summarizeEnvs, validateDeploymentListFilter, withoutArchiveStamp, } from './records.js';
8
9
  import { validateSlug, validateTenantRef } from './validate.js';
@@ -10,8 +11,9 @@ import { validateSlug, validateTenantRef } from './validate.js';
10
11
  export class InMemoryArtifactStore {
11
12
  #records = new Map();
12
13
  #productionEnvironments = new Map();
13
- async append(record) {
14
+ async append(record, precondition) {
14
15
  assertDeploymentAppendVersion([...this.#records.values()], record);
16
+ assertDeploymentAppendPolicy([...this.#records.values()], record, precondition);
15
17
  const existing = this.#records.get(record.deploymentId);
16
18
  if (existing !== undefined)
17
19
  assertSameAppPackageSnapshot(existing, record);
@@ -85,8 +87,7 @@ export class InMemoryArtifactStore {
85
87
  if (target === undefined || !sameTenantRecord(target, safe))
86
88
  return Promise.resolve(undefined);
87
89
  assertDeploymentActivationUnlocked([...this.#records.values()], target);
88
- if (target.schemaVersion !== (precondition?.expectedSchemaVersion ?? 1) ||
89
- (precondition !== undefined && target.accessMode !== precondition.expectedAccessMode)) {
90
+ if (!matchesDeploymentActivation(target, precondition)) {
90
91
  return Promise.resolve(undefined);
91
92
  }
92
93
  const previousActive = [...this.#records.values()]
@@ -95,6 +96,7 @@ export class InMemoryArtifactStore {
95
96
  if (precondition === undefined && previousActive !== undefined) {
96
97
  assertDeploymentAppendVersion([previousActive], { ...target, active: true });
97
98
  }
99
+ assertDeploymentAppendPolicy([...this.#records.values()], { ...target, active: true }, precondition?.expectedActivePolicy);
98
100
  const alreadyActive = previousActive?.deploymentId === target.deploymentId;
99
101
  const activated = {
100
102
  ...target,
@@ -1,5 +1,4 @@
1
- import type { AccessMode } from '@noodle-borg/transport-http';
2
- import type { ActiveAccessUpdateInput, AppArchiveResult, AppRestorePrecondition, AppRestoreResult, AppSummary, ArtifactStore, DeploymentActivationResult, DeploymentListFilter, DeploymentLock, DeploymentLockUpdateResult, DeploymentSummary, DeployRecord, EnvSummary, ProductionEnvironmentChange, TenantAuthConfig, TenantRef } from '../store.js';
1
+ import type { ActiveAccessUpdateInput, AppArchiveResult, AppRestorePrecondition, AppRestoreResult, AppSummary, ArtifactStore, DeploymentActivationPrecondition, DeploymentActivationResult, DeploymentListFilter, DeploymentLock, DeploymentLockUpdateResult, DeploymentPolicyPrecondition, DeploymentSummary, DeployRecord, EnvSummary, ProductionEnvironmentChange, TenantAuthConfig, TenantRef } from '../store.js';
3
2
  /**
4
3
  * JSON-file store: one file per deployment at `<dataDir>/deployments/<deploymentId>.json`, written atomically
5
4
  * (write a temp file, then `rename` over the target — no partial file is ever read). `deploymentId` is a
@@ -9,18 +8,14 @@ import type { ActiveAccessUpdateInput, AppArchiveResult, AppRestorePrecondition,
9
8
  export declare class JsonFileArtifactStore implements ArtifactStore {
10
9
  #private;
11
10
  constructor(dataDir: string);
12
- append(record: DeployRecord): Promise<void>;
11
+ append(record: DeployRecord, precondition?: DeploymentPolicyPrecondition): Promise<void>;
13
12
  get(deploymentId: string): Promise<DeployRecord | undefined>;
14
13
  getActiveByTenant(ref: TenantRef): Promise<DeployRecord | undefined>;
15
14
  getActiveByTenantVersion(ref: TenantRef, serverVersion: string): Promise<DeployRecord | undefined>;
16
15
  setDeploymentLock(ref: TenantRef, serverVersion: string, expectedDeploymentId: string, deploymentLock: DeploymentLock | undefined): Promise<DeploymentLockUpdateResult>;
17
16
  findActiveCustomerAuthAudienceConflict(ref: TenantRef, auth: TenantAuthConfig): Promise<TenantRef | undefined>;
18
17
  updateActiveAccess(ref: TenantRef, deploymentId: string, input: ActiveAccessUpdateInput): Promise<DeployRecord | undefined>;
19
- activateDeployment(ref: TenantRef, deploymentId: string, precondition?: {
20
- readonly expectedAccessMode: AccessMode | undefined;
21
- readonly expectedSchemaVersion?: number;
22
- readonly serverAuth?: TenantAuthConfig;
23
- }): Promise<DeploymentActivationResult | undefined>;
18
+ activateDeployment(ref: TenantRef, deploymentId: string, precondition?: DeploymentActivationPrecondition): Promise<DeploymentActivationResult | undefined>;
24
19
  loadAll(): Promise<readonly DeployRecord[]>;
25
20
  listDeployments(filter: DeploymentListFilter): Promise<readonly DeploymentSummary[]>;
26
21
  getAppArchivedAt(org: string, app: string): Promise<string | undefined>;
@@ -4,8 +4,9 @@ import { join } from 'node:path';
4
4
  import { normalizeServerVersion } from '@noodle-borg/module';
5
5
  import { assertSameAppPackageSnapshot, sanitizeDeployRecordAppPackageSnapshot, } from '../app-package-snapshot.js';
6
6
  import { assertCustomerAuthRestorePrecondition, assertUniqueActiveCustomerAuthAudienceBindings, findActiveCustomerAuthAudienceConflict, requiresCustomerAuthProjection, } from '../customer-auth-audience-binding.js';
7
+ import { matchesDeploymentActivation } from '../deployment-activation-precondition.js';
7
8
  import { assertDeploymentActivationUnlocked, assertDeploymentAppendUnlocked, } from '../deployment-lock.js';
8
- import { assertDeploymentAppendVersion } from '../deployment-record-version.js';
9
+ import { assertDeploymentAppendPolicy, assertDeploymentAppendVersion, } from '../deployment-record-version.js';
9
10
  import { defaultActiveRecord, sameDeploymentScope, sameTenantRecord, } from '../deployment-versioning.js';
10
11
  import { appArchivedAt, deploymentSummary, matchesDeploymentFilter, paginateAppSummaries, planAppArchive, planAppRestore, planArchivedAppSweep, preserveDeploymentOwnerState, resolveActiveAccessUpdate, resolveProductionEnvironment, summarizeApps, summarizeEnvs, validateDeploymentListFilter, withoutArchiveStamp, } from './records.js';
11
12
  import { DEPLOYMENT_ID_PATTERN, validateSlug, validateTenantRef } from './validate.js';
@@ -23,10 +24,10 @@ export class JsonFileArtifactStore {
23
24
  this.#dir = join(dataDir, 'deployments');
24
25
  this.#environmentMetadataDir = join(dataDir, 'environment-metadata');
25
26
  }
26
- append(record) {
27
- return this.#serializeLifecycle(() => this.#appendUnlocked(record));
27
+ append(record, precondition) {
28
+ return this.#serializeLifecycle(() => this.#appendUnlocked(record, precondition));
28
29
  }
29
- async #appendUnlocked(record) {
30
+ async #appendUnlocked(record, precondition) {
30
31
  // Defence in depth: `deploymentId` becomes a filename, so reject anything outside the minted shape
31
32
  // (`mintDeploymentId` only ever produces `[a-z0-9-]`). Guards this public class against a caller that
32
33
  // passes an untrusted id (no path traversal, no escaping the data dir).
@@ -36,6 +37,7 @@ export class JsonFileArtifactStore {
36
37
  }
37
38
  const records = await this.loadAll();
38
39
  assertDeploymentAppendVersion(records, record);
40
+ assertDeploymentAppendPolicy(records, record, precondition);
39
41
  const existing = records.find((candidate) => candidate.deploymentId === record.deploymentId);
40
42
  if (existing !== undefined)
41
43
  assertSameAppPackageSnapshot(existing, record);
@@ -129,8 +131,7 @@ export class JsonFileArtifactStore {
129
131
  const target = await this.get(deploymentId);
130
132
  if (target === undefined || !sameTenantRecord(target, safe))
131
133
  return undefined;
132
- if (target.schemaVersion !== (precondition?.expectedSchemaVersion ?? 1) ||
133
- (precondition !== undefined && target.accessMode !== precondition.expectedAccessMode)) {
134
+ if (!matchesDeploymentActivation(target, precondition)) {
134
135
  return undefined;
135
136
  }
136
137
  const records = await this.loadAll();
@@ -141,6 +142,7 @@ export class JsonFileArtifactStore {
141
142
  if (precondition === undefined && previousActive !== undefined) {
142
143
  assertDeploymentAppendVersion([previousActive], { ...target, active: true });
143
144
  }
145
+ assertDeploymentAppendPolicy(records, { ...target, active: true }, precondition?.expectedActivePolicy);
144
146
  const alreadyActive = previousActive?.deploymentId === target.deploymentId;
145
147
  const activated = {
146
148
  ...target,
@@ -141,17 +141,28 @@ export function resolveActiveAccessUpdate(record, ref, input) {
141
141
  !sameTenantRecord(record, ref) ||
142
142
  record.accessMode !== input.expectedAccessMode ||
143
143
  record.schemaVersion !== (input.expectedSchemaVersion ?? 1) ||
144
+ (input.expectedManifest !== undefined && record.manifest !== input.expectedManifest) ||
144
145
  deploymentOwnerSubject(record) !== input.expectedOwnerSubject)
145
146
  return undefined;
146
147
  if ((record.accessMode ?? 'owner-only') === input.accessMode &&
148
+ (input.schemaVersion === undefined || record.schemaVersion === input.schemaVersion) &&
147
149
  (input.ownerSubject === undefined || deploymentOwnerSubject(record) === input.ownerSubject) &&
148
- !requiresCustomerAuthProjection({ ...record, accessMode: input.accessMode }, input.serverAuth))
150
+ !requiresCustomerAuthProjection({
151
+ ...record,
152
+ accessMode: input.accessMode,
153
+ schemaVersion: input.schemaVersion ?? record.schemaVersion,
154
+ }, input.serverAuth))
149
155
  return record;
150
156
  return {
151
157
  ...record,
152
158
  accessMode: input.accessMode,
159
+ schemaVersion: input.schemaVersion ?? record.schemaVersion,
153
160
  ...(input.ownerSubject !== undefined ? { ownerSubject: input.ownerSubject } : {}),
154
- ...(requiresCustomerAuthProjection({ ...record, accessMode: input.accessMode }, input.serverAuth) && input.serverAuth !== undefined
161
+ ...(requiresCustomerAuthProjection({
162
+ ...record,
163
+ accessMode: input.accessMode,
164
+ schemaVersion: input.schemaVersion ?? record.schemaVersion,
165
+ }, input.serverAuth) && input.serverAuth !== undefined
155
166
  ? { serverAuth: input.serverAuth }
156
167
  : {}),
157
168
  };
@@ -33,7 +33,7 @@ export type SecretEnvelope = {
33
33
  * of the source strings, so the inputs + identity are a faithful, reproducible record.
34
34
  */
35
35
  export interface DeployRecord {
36
- /** Record format version, for forward migration of the persisted shape. Current writers emit `1`. */
36
+ /** Record format version; customer mixed adoption writes 2, which remains sticky on new records. */
37
37
  readonly schemaVersion: number;
38
38
  readonly deploymentId: string;
39
39
  readonly orgSlug: string;
@@ -258,13 +258,29 @@ export interface DeploymentActivationResult {
258
258
  readonly alreadyActive: boolean;
259
259
  }
260
260
  /** One atomic access/owner compare-and-set over the active deployment's observed state. */
261
+ export interface DeploymentPolicyPrecondition {
262
+ readonly active: Pick<DeployRecord, 'deploymentId' | 'schemaVersion' | 'accessMode' | 'ownerSubject' | 'manifest' | 'serverAuth'> | null;
263
+ }
264
+ /** The source and authority validated before atomically activating this exact target revision. */
265
+ export interface DeploymentActivationPrecondition {
266
+ readonly expectedAccessMode: AccessMode | undefined;
267
+ /** Older callers are restricted to schema-1 targets. */
268
+ readonly expectedSchemaVersion?: number;
269
+ readonly expectedRevision?: Pick<DeployRecord, 'manifest' | 'connectors' | 'hostedAssets' | 'serverAuth' | 'serverVersion' | 'ownerSubject' | 'createdBySubject' | 'orgMembershipSources' | 'archivedAt'>;
270
+ readonly expectedActivePolicy?: DeploymentPolicyPrecondition;
271
+ /** Compiler-authoritative projection, applied only after revision validation. */
272
+ readonly serverAuth?: TenantAuthConfig;
273
+ }
261
274
  export interface ActiveAccessUpdateInput {
275
+ /** Internal policy adoption; callers cannot author a deployment schema version. */
276
+ readonly schemaVersion?: 2;
262
277
  readonly accessMode: AccessMode;
263
278
  /** Desired owner mutation. Omission preserves the current effective owner; clearing is unsupported. */
264
279
  readonly ownerSubject?: string;
265
280
  readonly expectedAccessMode: AccessMode | undefined;
266
281
  /** Older callers omit this and are restricted to version-1 records. */
267
282
  readonly expectedSchemaVersion?: number;
283
+ readonly expectedManifest?: string;
268
284
  readonly expectedOwnerSubject: string | undefined;
269
285
  /** Compiler-authoritative projection persisted atomically when enabling customer access. */
270
286
  readonly serverAuth?: TenantAuthConfig;
@@ -286,7 +302,7 @@ export interface ControlPlaneStore extends OrganizationStore {
286
302
  * an earlier deployment without changing the tenant-facing URL.
287
303
  */
288
304
  export interface ArtifactStore {
289
- append(record: DeployRecord): Promise<void>;
305
+ append(record: DeployRecord, precondition?: DeploymentPolicyPrecondition): Promise<void>;
290
306
  loadAll(): Promise<readonly DeployRecord[]>;
291
307
  /**
292
308
  * Fetch one server's record by id, or `undefined` if absent. A point read used by lazy
@@ -304,12 +320,7 @@ export interface ArtifactStore {
304
320
  * bindings. Implementations may optimize this security lookup; registries fall back to `loadAll`.
305
321
  */
306
322
  findActiveCustomerAuthAudienceConflict?(ref: TenantRef, auth: TenantAuthConfig): Promise<TenantRef | undefined>;
307
- activateDeployment(ref: TenantRef, deploymentId: string, precondition?: {
308
- readonly expectedAccessMode: AccessMode | undefined;
309
- readonly expectedSchemaVersion?: number;
310
- /** Compiler-authoritative projection persisted atomically when activating customer access. */
311
- readonly serverAuth?: TenantAuthConfig;
312
- }, options?: {
323
+ activateDeployment(ref: TenantRef, deploymentId: string, precondition?: DeploymentActivationPrecondition, options?: {
313
324
  readonly automationId?: string;
314
325
  }): Promise<DeploymentActivationResult | undefined>;
315
326
  /**
@@ -39,7 +39,7 @@
39
39
  "dependencies": {
40
40
  "@modelcontextprotocol/sdk": "^1.29.0",
41
41
  "@noodle-borg/admission-limits": "0.0.0",
42
- "@noodle-borg/agent-kit": "0.102.0",
42
+ "@noodle-borg/agent-kit": "0.104.0",
43
43
  "@noodle-borg/app-package": "0.0.0",
44
44
  "@noodle-borg/assistant-gateway": "0.0.0",
45
45
  "@noodle-borg/auth": "0.0.0",
@@ -5,6 +5,7 @@ import { type TlsPosture } from './front-door.js';
5
5
  import { type Logger } from './logging.js';
6
6
  import { type PublicTenantRouting } from './routing.js';
7
7
  import type { HostedToolAuthorizationObserver } from './service-principal-authorization.js';
8
+ import { type TargetAuthentication } from './target-authentication.js';
8
9
  import type { HostedToolDispatchHook } from './tool-dispatch.js';
9
10
  export type { AccessMode, AdmissionCategory, AdmissionContext, AdmissionDecision, AdmissionGate, DataPlaneAuthorizationResult, DataPlaneIdentityAuthorizer, OwnerTokenVerifier, TenantRouteRef, } from '@noodle-borg/module';
10
11
  export type { PublicTenantRouting } from './routing.js';
@@ -78,9 +79,7 @@ export interface ServedTarget {
78
79
  readonly app?: string;
79
80
  readonly environment?: string;
80
81
  readonly orgMembershipSources?: readonly OrgMembershipSource[];
81
- readonly verifyToken?: OwnerTokenVerifier;
82
- readonly authServerIssuer?: string;
83
- readonly authServerIssuers?: readonly string[];
82
+ readonly authentication?: TargetAuthentication;
84
83
  readonly intentCaptureMode?: IntentCaptureMode;
85
84
  }
86
85
  export type ServerLookup = (deploymentId: string) => Promise<ServedTarget | undefined>;
@@ -4,6 +4,7 @@ import { noopLogger } from './logging.js';
4
4
  import { guard, logRequest, rpcError, sendJson } from './responses.js';
5
5
  import { resolveRoute } from './routing.js';
6
6
  import { serveRequest } from './serve-request.js';
7
+ import { resolveTargetAuthentication } from './target-authentication.js';
7
8
  const DEFAULT_ENDPOINT = '/mcp';
8
9
  const DEFAULT_MAX_BODY = 1 << 20;
9
10
  /** Build the single-artifact raw-Node security front door. */
@@ -33,7 +34,7 @@ export function createMcpHttpHandler(target, options = {}) {
33
34
  app: undefined,
34
35
  environment: undefined,
35
36
  orgMembershipSources: undefined,
36
- verifyOwnerToken: options.verifyOwnerToken,
37
+ authentication: resolveTargetAuthentication({}, options.verifyOwnerToken),
37
38
  authorizeDataPlaneIdentity: options.authorizeDataPlaneIdentity,
38
39
  admissionGate: options.admissionGate,
39
40
  captureRequestEvent: options.captureRequestEvent,
@@ -101,9 +102,7 @@ export function createMcpRouter(lookup, options = {}) {
101
102
  ownerSubject: resolvedTarget.ownerSubject,
102
103
  org: resolvedTarget.org,
103
104
  orgMembershipSources: resolvedTarget.orgMembershipSources,
104
- verifyOwnerToken: resolvedTarget.accessMode === 'customers'
105
- ? resolvedTarget.verifyToken
106
- : (resolvedTarget.verifyToken ?? options.verifyOwnerToken),
105
+ authentication: resolveTargetAuthentication(resolvedTarget, options.verifyOwnerToken),
107
106
  authorizeDataPlaneIdentity: options.authorizeDataPlaneIdentity,
108
107
  admissionGate: options.admissionGate,
109
108
  captureRequestEvent: options.captureRequestEvent,
@@ -1,12 +1,13 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
- import type { AccessMode, DataPlaneIdentityAuthorizer, OrgMembershipSource, OwnerTokenVerifier } from '@noodle-borg/module';
2
+ import type { AccessMode, DataPlaneIdentityAuthorizer, OrgMembershipSource } from '@noodle-borg/module';
3
3
  import { type ProtocolRequestContext } from '@noodle-borg/protocol';
4
+ import type { TargetAuthentication } from './target-authentication.js';
4
5
  export interface IdentityAuthorizationOptions {
5
6
  readonly accessMode: AccessMode | undefined;
6
7
  readonly ownerSubject: string | undefined;
7
8
  readonly org: string | undefined;
8
9
  readonly orgMembershipSources: readonly OrgMembershipSource[] | undefined;
9
- readonly verifyOwnerToken: OwnerTokenVerifier | undefined;
10
+ readonly authentication: TargetAuthentication;
10
11
  readonly authorizeDataPlaneIdentity: DataPlaneIdentityAuthorizer | undefined;
11
12
  /** Whether to trust forwarded-proto when reconstructing the public resource/PRM URLs. */
12
13
  readonly trustProxy: boolean;
@@ -9,13 +9,13 @@ import { header } from './request-capture.js';
9
9
  export async function authorizeIdentityMode(req, res, auth) {
10
10
  const challenge = protectedResourceMetadataUrl(req, auth);
11
11
  const token = bearerToken(header(req, 'authorization'));
12
- if (token === null || auth.verifyOwnerToken === undefined) {
12
+ if (token === null || auth.authentication.verifyToken === undefined) {
13
13
  sendUnauthorized(res, challenge);
14
14
  return { allow: false, reason: token === null ? 'missing_token' : 'verifier_unavailable' };
15
15
  }
16
- const verification = await auth.verifyOwnerToken(token, canonicalResourceUrl(req, auth));
16
+ const verification = await auth.authentication.verifyToken(token, canonicalResourceUrl(req, auth));
17
17
  if (verification === null) {
18
- sendUnauthorized(res, challenge);
18
+ sendUnauthorized(res, challenge, 'invalid_token');
19
19
  return { allow: false, reason: 'token_rejected' };
20
20
  }
21
21
  const { caller: identity } = verification;
@@ -76,27 +76,38 @@ export async function authorizeIdentityMode(req, res, auth) {
76
76
  */
77
77
  export async function authorizePublicServiceMode(req, auth) {
78
78
  const token = bearerToken(header(req, 'authorization'));
79
- if (token === null || auth.verifyOwnerToken === undefined)
79
+ if (token === null || auth.authentication.verifyToken === undefined)
80
80
  return { allow: true };
81
- const verification = await auth.verifyOwnerToken(token, canonicalResourceUrl(req, auth));
81
+ const verification = await auth.authentication.verifyToken(token, canonicalResourceUrl(req, auth));
82
82
  return verification?.caller.identityKind === 'service'
83
83
  ? { allow: true, caller: verification.caller }
84
84
  : { allow: true };
85
85
  }
86
86
  export async function authorizeMixedMode(req, res, auth) {
87
- const token = bearerToken(header(req, 'authorization'));
88
- if (token === null)
87
+ const authorization = header(req, 'authorization');
88
+ if (authorization === undefined)
89
89
  return { allow: true };
90
+ const token = bearerToken(authorization);
90
91
  const challenge = protectedResourceMetadataUrl(req, auth);
91
- if (auth.verifyOwnerToken === undefined) {
92
+ if (token === null) {
93
+ sendUnauthorized(res, challenge, 'invalid_token');
94
+ return { allow: false, reason: 'token_rejected' };
95
+ }
96
+ if (auth.authentication.verifyToken === undefined) {
92
97
  sendUnauthorized(res, challenge);
93
98
  return { allow: false, reason: 'verifier_unavailable' };
94
99
  }
95
- const verification = await auth.verifyOwnerToken(token, canonicalResourceUrl(req, auth));
100
+ const verification = await auth.authentication.verifyToken(token, canonicalResourceUrl(req, auth));
96
101
  if (verification === null) {
97
- sendUnauthorized(res, challenge);
102
+ sendUnauthorized(res, challenge, 'invalid_token');
98
103
  return { allow: false, reason: 'token_rejected' };
99
104
  }
105
+ if (auth.authentication.kind === 'customer' &&
106
+ verification.caller.identityKind !== 'customer' &&
107
+ verification.caller.identityKind !== 'service') {
108
+ sendUnauthorized(res, challenge, 'invalid_token');
109
+ return { allow: false, reason: 'identity_not_customer' };
110
+ }
100
111
  return {
101
112
  allow: true,
102
113
  caller: verification.caller,
@@ -126,13 +137,13 @@ export function bearerToken(headerValue) {
126
137
  * `resourceMetadataUrl` is included so an MCP client can discover the authorization server (RFC 9728 /
127
138
  * the MCP authorization spec).
128
139
  */
129
- function sendUnauthorized(res, resourceMetadataUrl) {
140
+ function sendUnauthorized(res, resourceMetadataUrl, error) {
130
141
  const challenge = resourceMetadataUrl !== undefined
131
142
  ? `Bearer realm="noodle", resource_metadata="${resourceMetadataUrl}"`
132
143
  : 'Bearer realm="noodle"';
133
144
  res.writeHead(401, {
134
145
  'content-type': 'application/json; charset=utf-8',
135
- 'www-authenticate': challenge,
146
+ 'www-authenticate': error === undefined ? challenge : `${challenge}, error="${error}"`,
136
147
  });
137
148
  res.end(JSON.stringify(rpcError(JSON_RPC.INVALID_REQUEST, 'unauthorized')));
138
149
  }
@@ -8,6 +8,7 @@ export { sendJson } from './responses.js';
8
8
  export { type RunningServer, type ServeOptions, serveHttp } from './serve.js';
9
9
  export type { HostedToolAuthorizationObservation, HostedToolAuthorizationObserver, ServicePrincipalToolAuthorizationObservation, } from './service-principal-authorization.js';
10
10
  export { preflightServicePrincipalToolCall, sendServicePrincipalToolDenial, } from './service-principal-authorization.js';
11
+ export type { TargetAuthentication } from './target-authentication.js';
11
12
  export type { HostedToolDispatchContext, HostedToolDispatchHook, } from './tool-dispatch.js';
12
13
  export { toWebRequest, writeWebResponse } from './web-bridge.js';
13
14
  //# sourceMappingURL=index.d.ts.map
@@ -49,6 +49,9 @@ export async function serveRequest(req, res, target, auth, routeId, maxBody, all
49
49
  // and the SDK transport is never constructed for it. A missing accessMode is reserved for loopback/local
50
50
  // dev targets and remains open.
51
51
  let protocolContext = {
52
+ ...(auth.accessMode === 'mixed' && auth.authentication.kind === 'customer'
53
+ ? { toolAuthentication: 'mixed-customer' }
54
+ : {}),
52
55
  ...(auth.oauthClientCredentialsReady ? { oauthClientCredentialsReady: true } : {}),
53
56
  };
54
57
  let subject;
@@ -0,0 +1,15 @@
1
+ import type { AccessMode, OwnerTokenVerifier } from '@noodle-borg/module';
2
+ /** Resolved identity authority. A missing customer verifier must never select platform defaults. */
3
+ export type TargetAuthentication = {
4
+ readonly kind: 'platform';
5
+ readonly verifyToken?: OwnerTokenVerifier;
6
+ } | {
7
+ readonly kind: 'customer';
8
+ readonly verifyToken?: OwnerTokenVerifier;
9
+ readonly authorizationServers?: readonly string[];
10
+ };
11
+ export declare function resolveTargetAuthentication(target: {
12
+ readonly accessMode?: AccessMode;
13
+ readonly authentication?: TargetAuthentication;
14
+ }, platformVerifier?: OwnerTokenVerifier): TargetAuthentication;
15
+ //# sourceMappingURL=target-authentication.d.ts.map
@@ -0,0 +1,9 @@
1
+ export function resolveTargetAuthentication(target, platformVerifier) {
2
+ if (target.authentication?.kind === 'customer')
3
+ return target.authentication;
4
+ if (target.accessMode === 'customers')
5
+ return { kind: 'customer' };
6
+ const verifyToken = target.authentication?.verifyToken ?? platformVerifier;
7
+ return { kind: 'platform', ...(verifyToken === undefined ? {} : { verifyToken }) };
8
+ }
9
+ //# sourceMappingURL=target-authentication.js.map
@@ -124,6 +124,11 @@ export declare const deploySuccessResponseSchema: z.ZodObject<{
124
124
  mixed: "mixed";
125
125
  customers: "customers";
126
126
  }>;
127
+ authentication: z.ZodOptional<z.ZodEnum<{
128
+ customer: "customer";
129
+ none: "none";
130
+ platform: "platform";
131
+ }>>;
127
132
  ownerSubject: z.ZodOptional<z.ZodString>;
128
133
  url: z.ZodString;
129
134
  defaultUrl: z.ZodString;
@@ -385,4 +390,5 @@ export * from './application-connections.js';
385
390
  export * from './application-onboarding.js';
386
391
  export * from './billing-catalog.js';
387
392
  export * from './operation-coordination.js';
393
+ export { MIXED_CUSTOMER_AUTH_FEATURE_VERSION, type ServiceInfoClientResponse, type ServiceInfoResponse, serviceInfoClientResponseSchema, serviceInfoResponseSchema, } from './service-info.js';
388
394
  //# sourceMappingURL=index.d.ts.map
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import { z } from 'zod';
11
11
  import { accessModeSchema } from './access-mode.js';
12
- import { deploymentOwnerSubjectSchema } from './control-plane.js';
12
+ import { deploymentAuthenticationSchema, deploymentOwnerSubjectSchema } from './control-plane.js';
13
13
  export * from './access-mode.js';
14
14
  export * from './app-purge-reconciliation.js';
15
15
  export * from './application-channels.js';
@@ -92,6 +92,8 @@ export const deploySuccessResponseSchema = z.object({
92
92
  deploymentId: z.string().min(1),
93
93
  serverVersion: z.string().min(1),
94
94
  accessMode: accessModeSchema,
95
+ /** Effective authority reported by capable services; absence means unknown on older services. */
96
+ authentication: deploymentAuthenticationSchema.optional(),
95
97
  ownerSubject: deploymentOwnerSubjectSchema.optional(),
96
98
  url: z.string().min(1),
97
99
  defaultUrl: z.string().min(1),
@@ -227,4 +229,5 @@ export * from './application-connections.js';
227
229
  export * from './application-onboarding.js';
228
230
  export * from './billing-catalog.js';
229
231
  export * from './operation-coordination.js';
232
+ export { MIXED_CUSTOMER_AUTH_FEATURE_VERSION, serviceInfoClientResponseSchema, serviceInfoResponseSchema, } from './service-info.js';
230
233
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,41 @@
1
+ import { z } from 'zod';
2
+ export declare const MIXED_CUSTOMER_AUTH_FEATURE_VERSION = 1;
3
+ /** Strict producer contract for the enabled R1 service; no configured tenant data belongs here. */
4
+ export declare const serviceInfoResponseSchema: z.ZodObject<{
5
+ developerPlugin: z.ZodOptional<z.ZodObject<{
6
+ mcpCapabilityVersion: z.ZodString;
7
+ }, z.core.$strict>>;
8
+ features: z.ZodObject<{
9
+ mixedCustomerAuth: z.ZodLiteral<1>;
10
+ }, z.core.$strict>;
11
+ ok: z.ZodLiteral<true>;
12
+ status: z.ZodLiteral<"ok">;
13
+ version: z.ZodString;
14
+ gitSha: z.ZodString;
15
+ buildTime: z.ZodString;
16
+ systemRelease: z.ZodOptional<z.ZodString>;
17
+ manifestChecksum: z.ZodOptional<z.ZodString>;
18
+ packageVersions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
19
+ compatiblePackageVersions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
20
+ }, z.core.$strict>;
21
+ /** Older services may omit features; future fields are stripped at each structured metadata layer. */
22
+ export declare const serviceInfoClientResponseSchema: z.ZodObject<{
23
+ developerPlugin: z.ZodOptional<z.ZodObject<{
24
+ mcpCapabilityVersion: z.ZodString;
25
+ }, z.core.$strip>>;
26
+ features: z.ZodOptional<z.ZodObject<{
27
+ mixedCustomerAuth: z.ZodOptional<z.ZodNumber>;
28
+ }, z.core.$strip>>;
29
+ ok: z.ZodLiteral<true>;
30
+ status: z.ZodLiteral<"ok">;
31
+ version: z.ZodString;
32
+ gitSha: z.ZodString;
33
+ buildTime: z.ZodString;
34
+ systemRelease: z.ZodOptional<z.ZodString>;
35
+ manifestChecksum: z.ZodOptional<z.ZodString>;
36
+ packageVersions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
37
+ compatiblePackageVersions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
38
+ }, z.core.$strip>;
39
+ export type ServiceInfoResponse = z.infer<typeof serviceInfoResponseSchema>;
40
+ export type ServiceInfoClientResponse = z.infer<typeof serviceInfoClientResponseSchema>;
41
+ //# sourceMappingURL=service-info.d.ts.map
@@ -0,0 +1,26 @@
1
+ import { z } from 'zod';
2
+ export const MIXED_CUSTOMER_AUTH_FEATURE_VERSION = 1;
3
+ const buildInfoShape = {
4
+ ok: z.literal(true),
5
+ status: z.literal('ok'),
6
+ version: z.string(),
7
+ gitSha: z.string(),
8
+ buildTime: z.string(),
9
+ systemRelease: z.string().optional(),
10
+ manifestChecksum: z.string().optional(),
11
+ packageVersions: z.record(z.string(), z.string()).optional(),
12
+ compatiblePackageVersions: z.record(z.string(), z.array(z.string())).optional(),
13
+ };
14
+ /** Strict producer contract for the enabled R1 service; no configured tenant data belongs here. */
15
+ export const serviceInfoResponseSchema = z.strictObject({
16
+ ...buildInfoShape,
17
+ developerPlugin: z.strictObject({ mcpCapabilityVersion: z.string().min(1) }).optional(),
18
+ features: z.strictObject({ mixedCustomerAuth: z.literal(MIXED_CUSTOMER_AUTH_FEATURE_VERSION) }),
19
+ });
20
+ /** Older services may omit features; future fields are stripped at each structured metadata layer. */
21
+ export const serviceInfoClientResponseSchema = z.object({
22
+ ...buildInfoShape,
23
+ developerPlugin: z.object({ mcpCapabilityVersion: z.string().min(1) }).optional(),
24
+ features: z.object({ mixedCustomerAuth: z.number().int().positive().optional() }).optional(),
25
+ });
26
+ //# sourceMappingURL=service-info.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noodleseed/one",
3
- "version": "0.166.0",
3
+ "version": "0.168.0",
4
4
  "private": false,
5
5
  "description": "Noodle CLI by Noodle Seed — author, run, and deploy declarative MCP servers. Embedding the assistant in your own web app is @noodleseed/assistant.",
6
6
  "license": "Apache-2.0",
@@ -235,7 +235,7 @@
235
235
  "@modelcontextprotocol/client": "2.0.0",
236
236
  "@modelcontextprotocol/server": "2.0.0",
237
237
  "@noodle-borg/admission-limits": "0.0.0",
238
- "@noodle-borg/agent-kit": "0.102.0",
238
+ "@noodle-borg/agent-kit": "0.104.0",
239
239
  "@noodle-borg/app-audit": "0.0.0",
240
240
  "@noodle-borg/app-package": "0.0.0",
241
241
  "@noodle-borg/assistant-gateway": "0.0.0",
@@ -1,7 +0,0 @@
1
- export function isIdentityAccessMode(accessMode) {
2
- return (accessMode === 'owner-only' ||
3
- accessMode === 'org-members' ||
4
- accessMode === 'customers' ||
5
- accessMode === 'authenticated');
6
- }
7
- //# sourceMappingURL=access-mode.js.map